package com.iplant.mes.serviceImpl;

import com.iplant.ams.service.basicenum.ams.fxt.AMSFrockType;
import com.iplant.ams.service.basicenum.dms.scada.DMSDataClass;
import com.iplant.ams.service.po.ams.fxt.DMSFrockLedger;
import com.iplant.ams.service.po.dms.DMSDeviceLedger;
import com.iplant.ams.service.po.dms.maintenance.DMSMaintenancePlan;
import com.iplant.ams.service.po.dms.scada.conf.DMSDeviceParameter;
import com.iplant.ams.service.po.dms.scada.para.DMSDeviceRealParameter;
import com.iplant.ams.service.po.dms.scada.record.DMSProcessRecord;
import com.iplant.ams.serviceImpl.DMSServiceImpl;
import com.iplant.ams.serviceImpl.DRMServiceImpl;
import com.iplant.ams.serviceImpl.dao.dms.scada.DMSDeviceRealParameterDAO;
import com.iplant.aps.service.apsenum.aps.APSFirstCheckStatus;
import com.iplant.aps.service.apsenum.aps.APSTaskStatus;
import com.iplant.aps.service.apsenum.oms.OMSOrderStatus;
import com.iplant.aps.service.po.aps.APSTaskPart;
import com.iplant.aps.service.po.aps.APSTaskPartPoint;
import com.iplant.aps.service.po.aps.APSTaskPartPointDay;
import com.iplant.aps.service.po.oms.OMSOrder;
import com.iplant.aps.serviceImpl.MRPServiceImpl;
import com.iplant.aps.serviceImpl.dao.aps.APSTaskPartPointDayDAO;
import com.iplant.base.po.OutResult;
import com.iplant.base.po.Pagination;
import com.iplant.base.po.ServiceResult;
import com.iplant.base.po.enums.ExceptionTypes;
import com.iplant.base.po.excel.ExcelCellData;
import com.iplant.base.utils.RetCode;
import com.iplant.base.utils.cache.CacheUtils;
import com.iplant.base.utils.general.CalendarTool;
import com.iplant.base.utils.general.Configuration;
import com.iplant.base.utils.general.StringUtils;
import com.iplant.base.utils.sql.orm.ORMUtils;
import com.iplant.base.utils.sql.orm.PoService;
import com.iplant.base.utils.tasker.TaskTimerUtils;
import com.iplant.basic.service.basicenum.bfc.BaseTagType;
import com.iplant.basic.service.basicenum.fmc.FMCBarCodeType;
import com.iplant.basic.service.po.bfc.BFCMessage;
import com.iplant.basic.service.po.bms.BMSClassInfo;
import com.iplant.basic.service.po.bms.BMSClassRange;
import com.iplant.basic.service.po.bms.BMSEmployee;
import com.iplant.basic.service.po.fmc.FMCCodeScanningGunConfig;
import com.iplant.basic.service.po.fmc.FMCIPConfig;
import com.iplant.basic.service.po.fmc.FMCShift;
import com.iplant.basic.service.po.fmc.FMCStation;
import com.iplant.basic.serviceImpl.FMCServiceImpl;
import com.iplant.basic.serviceImpl.dao.bfc.BFCSystemConfigDAO;
import com.iplant.basic.utils.Constants;
import com.iplant.basic.utils.ExcelUtil;
import com.iplant.mes.service.SFCService;
import com.iplant.mes.service.mesenum.exc.EXCProductRepairItemStatus;
import com.iplant.mes.service.po.bcm.BCMRule;
import com.iplant.mes.service.po.bcm.BCMRuleCode;
import com.iplant.mes.service.po.exc.EXCLineStop;
import com.iplant.mes.service.po.exc.EXCProductRepair;
import com.iplant.mes.service.po.exc.EXCProductRepairItem;
import com.iplant.mes.service.po.print.SFCFactoryTest;
import com.iplant.mes.service.po.print.SFCFactoryTestCode;
import com.iplant.mes.service.po.print.SFCFactoryTestItem;
import com.iplant.mes.service.po.qms.QMSMaterialCheck;
import com.iplant.mes.service.po.sfc.autoStation.*;
import com.iplant.mes.serviceImpl.dao.sfc.SFCTaskSelfDAO;
import com.iplant.mes.serviceImpl.dao.sfc.SFCWorkpieceRecordDAO;
import com.iplant.mes.serviceImpl.utils.WMSAutoUtils;
import com.iplant.pdm.service.basicenum.fpc.FPCParamType;
import com.iplant.pdm.service.basicenum.fpc.FPCTaskReportMode;
import com.iplant.pdm.service.basicenum.pdm.PDMComponentTypes;
import com.iplant.mes.service.mesenum.sfc.*;
import com.iplant.mes.service.po.ipt.IPTValue;
import com.iplant.mes.service.po.sfc.*;
import com.iplant.pdm.service.basicenum.fpc.FPCCheckType;
import com.iplant.pdm.service.basicenum.fpc.FPCStandardType;
import com.iplant.pdm.service.po.bop.FPCRoutePartPoint;
import com.iplant.pdm.service.po.bop.partpoint.FPCRoutePartPointParameter;
import com.iplant.pdm.service.po.bop.partpoint.FPCRouteStep;
import com.iplant.pdm.service.po.fpc.FPCProduct;
import com.iplant.pdm.service.po.fpc.FPCProductCom;
import com.iplant.pdm.service.po.mss.MSSBOMItem;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SFCServiceImpl implements SFCService {

    //region 构造函数
    public SFCServiceImpl() {
    }
    //endregion

    //region 单实例

    private static SFCService Instance = null;

    public static SFCService getInstance() {
        if (Instance == null)
            Instance = new SFCServiceImpl();

        return Instance;
    }

    //endregion

    //region POService
    PoService<SFCTaskSelf> mSFCTaskSelfService = ORMUtils.GetPoService(SFCTaskSelf.class);
    PoService<APSTaskPartPointDay> mAPSTaskPartPointDayService = ORMUtils.GetPoService(APSTaskPartPointDay.class);
    PoService<SFCLoginEvent> mSFCLoginEventService = ORMUtils.GetPoService(SFCLoginEvent.class);
    PoService<SFCTaskIPT> mSFCTaskIPTService = ORMUtils.GetPoService(SFCTaskIPT.class);
    PoService<BMSClassRange> mBMSClassRangeService = ORMUtils.GetPoService(BMSClassRange.class);
    PoService<FPCRouteStep> mFPCRouteStepService = ORMUtils.GetPoService(FPCRouteStep.class);
    PoService<IPTValue> mIPTValueService = ORMUtils.GetPoService(IPTValue.class);
    PoService<FPCRoutePartPoint> mFPCRoutePartPointService = ORMUtils.GetPoService(FPCRoutePartPoint.class);
    PoService<SFCWorkpieceRecord> mSFCWorkpieceRecordService = ORMUtils.GetPoService(SFCWorkpieceRecord.class);
    PoService<SFCTaskReport> mSFCTaskReportService = ORMUtils.GetPoService(SFCTaskReport.class);
    PoService<APSTaskPartPoint> mAPSTaskPartPointService = ORMUtils.GetPoService(APSTaskPartPoint.class);
    PoService<APSTaskPart> mAPSTaskPartService = ORMUtils.GetPoService(APSTaskPart.class);
    PoService<SFCFrockBindRecord> mSFCFrockBindRecordService = ORMUtils.GetPoService(SFCFrockBindRecord.class);
    PoService<MSSBOMItem> mMSSBOMItemService = ORMUtils.GetPoService(MSSBOMItem.class);
    PoService<SFCStationMaterial> mSFCStationMaterialService = ORMUtils.GetPoService(SFCStationMaterial.class);
    PoService<SFCBoxReport> mSFCBoxReportService = ORMUtils.GetPoService(SFCBoxReport.class);
    PoService<OMSOrder> mOMSOrderService = ORMUtils.GetPoService(OMSOrder.class);
    PoService<DMSFrockLedger> mDMSFrockLedgerService = ORMUtils.GetPoService(DMSFrockLedger.class);
    PoService<DMSDeviceLedger> mDMSDeviceLedgerService = ORMUtils.GetPoService(DMSDeviceLedger.class);
    PoService<SFCWorkpieceResume> mSFCWorkpieceResumeService = ORMUtils.GetPoService(SFCWorkpieceResume.class);
    PoService<SFCMaterialLoadingRecord> mSFCMaterialLoadingRecordService =
            ORMUtils.GetPoService(SFCMaterialLoadingRecord.class);
    PoService<SFCTaskStartCheck> mSFCTaskStartCheckService = ORMUtils.GetPoService(SFCTaskStartCheck.class);
    PoService<BMSClassInfo> mBMSClassInfoService = ORMUtils.GetPoService(BMSClassInfo.class);

    PoService<FMCStation> mFMCStationService = ORMUtils.GetPoService(FMCStation.class);
    PoService<FMCIPConfig> mFMCIPConfigService = ORMUtils.GetPoService(FMCIPConfig.class);
    PoService<FMCCodeScanningGunConfig> mFMCCodeScanningGunConfigService =
            ORMUtils.GetPoService(FMCCodeScanningGunConfig.class);
    PoService<SFCAssemblyRecord> mSFCAssemblyRecordService =
            ORMUtils.GetPoService(SFCAssemblyRecord.class);
    PoService<EXCProductRepairItem> mEXCProductRepairItemService =
            ORMUtils.GetPoService(EXCProductRepairItem.class);
    PoService<EXCProductRepair> mEXCProductRepairService = ORMUtils.GetPoService(EXCProductRepair.class);
    PoService<FPCProductCom> mFPCProductComService = ORMUtils.GetPoService(FPCProductCom.class);
    PoService<SFCTaskParamItem> mSFCTaskParamItemService = ORMUtils.GetPoService(SFCTaskParamItem.class);
    PoService<SFCFactoryTestCode> mSFCFactoryTestCodeService = ORMUtils.GetPoService(SFCFactoryTestCode.class);
    PoService<FPCProduct> mFPCProductService = ORMUtils.GetPoService(FPCProduct.class);
    PoService<BCMRule> mBCMRuleService = ORMUtils.GetPoService(BCMRule.class);
    PoService<EXCLineStop> mEXCLineStopService = ORMUtils.GetPoService(EXCLineStop.class);
    PoService<FPCRoutePartPointParameter> mFPCRoutePartPointParameterService =
            ORMUtils.GetPoService(FPCRoutePartPointParameter.class);
    PoService<DMSDeviceParameter> mDMSDeviceParameterService = ORMUtils.GetPoService(DMSDeviceParameter.class);
    PoService<SFCStationEnvironment> mSFCStationEnvironmentService = ORMUtils.GetPoService(SFCStationEnvironment.class);
    PoService<SFCIPTEnvironmentRecord> mSFCIPTEnvironmentRecordService =
            ORMUtils.GetPoService(SFCIPTEnvironmentRecord.class);
    PoService<QMSMaterialCheck> mQMSMaterialCheckService = ORMUtils.GetPoService(QMSMaterialCheck.class);
    PoService<SFCToolUsageRecord> mSFCToolUsageRecordService = ORMUtils.GetPoService(SFCToolUsageRecord.class);
    //endregion

    //region 派工

    @Override
    public ServiceResult<Integer> SFC_SaveDispatchResult(BMSEmployee wLoginUser, List<SFCTaskSelf> wSFCTaskSelfList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            List<BFCMessage> wBFCMessageList = new ArrayList<>();
            BFCMessage wMessage = null;
            SimpleDateFormat wSDF = new SimpleDateFormat("yyyyMMdd");
            int wShiftID = Integer.parseInt(wSDF.format(Calendar.getInstance().getTime()));

            for (SFCTaskSelf wSFCTaskSelf : wSFCTaskSelfList) {
                wSFCTaskSelf.CreatorID = wLoginUser.ID;
                wSFCTaskSelf.EditorID = wLoginUser.ID;
                wSFCTaskSelf.Status = SFCTaskStatus.Todo.getValue();
                wSFCTaskSelf.Active = 1;
                mSFCTaskSelfService.UpdatePo(wLoginUser, wSFCTaskSelf, wErrorCode);

                if (wSFCTaskSelf.ID <= 0) {
                    wResult.FaultCode += "提示：数据保存失败!";
                    return wResult;
                }
            }

            //更新日计划状态为`派工`
            Map<String, Object> wParams = new HashMap<String, Object>();
            List<Integer> wTaskPartPointDayIDList =
                    wSFCTaskSelfList.stream().map(p -> p.TaskPartPointDayID).distinct().collect(Collectors.toList());
            List<APSTaskPartPointDay> wAPSTaskPartPointDayList = mAPSTaskPartPointDayService.SelectPOList(wLoginUser,
                    wTaskPartPointDayIDList, wParams, Pagination.getNewMaxSize(), wErrorCode);
            for (APSTaskPartPointDay wAPSTaskPartPointDay : wAPSTaskPartPointDayList) {
                wAPSTaskPartPointDay.EditorID = wLoginUser.ID;
                wAPSTaskPartPointDay.Status = APSTaskStatus.Confirm.getValue();
                mAPSTaskPartPointDayService.UpdatePo(wLoginUser, wAPSTaskPartPointDay, wErrorCode);
            }

            //维护上道工序的报工单确认人
            TaskTimerUtils.AddTask("UpdateTaskReportConfirmList",
                    () -> UpdateTaskReportConfirmList(wLoginUser, wSFCTaskSelfList, wErrorCode)
            );

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 维护上道工序的报工单确认人
     */
    private void UpdateTaskReportConfirmList(BMSEmployee wLoginUser, List<SFCTaskSelf> wSFCTaskSelfList,
                                             OutResult<Integer> wErrorCode) {
        try {
            List<Integer> wTaskPartPointIDList =
                    wSFCTaskSelfList.stream().map(p -> p.TaskPartPointID).distinct().collect(Collectors.toList());

            Map<String, Object> wParams = new HashMap<String, Object>();
            for (int wTaskPartPointID : wTaskPartPointIDList) {
                wParams.clear();
                wParams.put("ID", wTaskPartPointID);
                APSTaskPartPoint wAPSTaskPartPoint = mAPSTaskPartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wAPSTaskPartPoint == null || wAPSTaskPartPoint.ID == 0) {
                    continue;
                }
                //查询工艺工序
                wParams.clear();
                wParams.put("ID", wAPSTaskPartPoint.RoutePartPointID);
                FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID == 0 || wFPCRoutePartPoint.PrevPartPointID <= 0) {
                    continue;
                }
                //查询报工单
                wParams.clear();
                wParams.put("OrderID", wAPSTaskPartPoint.OrderID);
                wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                wParams.put("Status", SFCTaskReportStatus.TO_CONFIRM.getValue());
                List<SFCTaskReport> wSFCTaskReportList = mSFCTaskReportService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(), wErrorCode);
                if (wSFCTaskReportList == null || wSFCTaskReportList.size() == 0) {
                    continue;
                }
                //根据工序任务查询今日派工单
                wParams.clear();
                wParams.put("OrderID", wAPSTaskPartPoint.OrderID);
                wParams.put("RoutePartPointID", wAPSTaskPartPoint.RoutePartPointID);
                wParams.put("TaskPartPointID", wTaskPartPointID);
                wParams.put("Active", 1);
                wParams.put("StaStartTime", CalendarTool.getDate());
                wParams.put("StaEndTime", CalendarTool.getDateMaxMill());
                List<SFCTaskSelf> wTodayList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                if (wTodayList == null || wTodayList.size() == 0) {
                    continue;
                }
                List<Integer> wWorkerList =
                        wTodayList.stream().map(p -> p.WorkerID).distinct().collect(Collectors.toList());
                //重新赋值待确认人
                for (SFCTaskReport wSFCTaskReport : wSFCTaskReportList) {
                    wSFCTaskReport.HandoverList = wWorkerList;
                    wSFCTaskReport.setUserInfo(wLoginUser);
                }
                mSFCTaskReportService.UpdatePoList(wLoginUser, wSFCTaskReportList, wErrorCode);
            }

        } catch (Exception ex) {
            log.error("UpdateTaskReportConfirmList", ex);
        }
    }

    @Override
    public ServiceResult<List<SFCTaskSelf>> SFC_QuerySFCTaskSelfCurrent(BMSEmployee wLoginUser, String wStationCode,
                                                                        int wIsAll, int wIsUserAll) {
        ServiceResult<List<SFCTaskSelf>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("Active", 1);
            List<FMCStation> wFMCStationList = mFMCStationService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wFMCStationList == null || wFMCStationList.size() == 0) {
                return wResult;
            }

            List<String> wStationCodeList = StringUtils.parseList(wStationCode.split(","));
            wFMCStationList =
                    wFMCStationList.stream().filter(p -> wStationCodeList.stream().anyMatch(q -> q.equals(p.Code))).collect(Collectors.toList());
            if (wFMCStationList == null || wFMCStationList.size() == 0) {
                return wResult;
            }

            String wStationIDs = StringUtils.Join(",",
                    wFMCStationList.stream().map(p -> p.ID).collect(Collectors.toList()));

            wParams.clear();
            wParams.put("StationID", wStationIDs);
            if (wIsAll == 1) {
                wParams.put("Status", "1,2,3,4,5");
            } else {
                wParams.put("Status", SFCTaskStatus.Doing.getValue());
            }
            wParams.put("StaStartTime", CalendarTool.getDate());
            wParams.put("StaEndTime", CalendarTool.getDateMaxMill());
            if (wIsUserAll != 1) {
                wParams.put("WorkerID", wLoginUser.ID);
            }
            wResult.Result = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            //查询历史已开工的工单
            List<SFCTaskSelf> wHistoryList =
                    SFCFrockBindServiceImpl.getInstance().SFC_QuerHistoryStartTaskSelfList(wLoginUser, -1,
                            wFMCStationList.stream().map(p -> p.ID).collect(Collectors.toList()), wIsUserAll).Result;
            if (wHistoryList.size() > 0) {
                wResult.Result.addAll(wHistoryList);
            }

            if (wResult.Result.size() > 0) {
                //维护产出数量
                MaintenanceOutputQuantity(wLoginUser, wResult.Result, wErrorCode);
            }

            //工艺参数与物料清单查询
            if (wResult.Result.size() > 0) {
                List<Integer> wRoutePartPointIDList =
                        wResult.Result.stream().map(p -> p.RoutePartPointID).distinct().collect(Collectors.toList());
                //工艺参数
                wParams.clear();
                wParams.put("RoutePartPointID", StringUtils.Join(",", wRoutePartPointIDList));
                wParams.put("Active", 1);
                wParams.put("ParamType", FPCParamType.PROCESS_PARAMETERS.getValue());
                List<FPCRoutePartPointParameter> wFPCRoutePartPointParameterList =
                        mFPCRoutePartPointParameterService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                                wErrorCode);
                Map<Integer, List<FPCRoutePartPointParameter>> wRoutePartPointIDParameterListMap =
                        wFPCRoutePartPointParameterList.stream()
                                .collect(Collectors.groupingBy(FPCRoutePartPointParameter::getRoutePartPointID));
                //物料清单
                wParams.put("RoutePartPointID", StringUtils.Join(",", wRoutePartPointIDList));
                wParams.put("Active", 1);
                List<MSSBOMItem> wMSSBOMItemList = mMSSBOMItemService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                Map<Integer, List<MSSBOMItem>> wRoutePartPointIDBomListMap =
                        wMSSBOMItemList.stream()
                                .collect(Collectors.groupingBy(MSSBOMItem::getRoutePartPointID));
                //工位实时物料
                wParams.clear();
                wParams.put("StationID", wStationIDs);
                wParams.put("RoutePartPointID", StringUtils.Join(",", wRoutePartPointIDList));
                List<SFCStationMaterial> wSFCStationMaterialList = mSFCStationMaterialService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(),
                        wErrorCode);
                Map<Integer, List<SFCStationMaterial>> wStationMaterialListMap = wSFCStationMaterialList.stream()
                        .collect(Collectors.groupingBy(SFCStationMaterial::getStationID));

                for (SFCTaskSelf wSFCTaskSelf : wResult.Result) {
                    if (wRoutePartPointIDParameterListMap.containsKey(wSFCTaskSelf.RoutePartPointID)) {
                        //转换工艺参数
                        wSFCTaskSelf.ParameterList = SFC_GetParameterList(wLoginUser,
                                wRoutePartPointIDParameterListMap.get(wSFCTaskSelf.RoutePartPointID), wErrorCode);
                    }
                    if (wRoutePartPointIDBomListMap.containsKey(wSFCTaskSelf.RoutePartPointID)) {

                        List<SFCStationMaterial> wMaterialList = new ArrayList<>();
                        if (wStationMaterialListMap.containsKey(wSFCTaskSelf.StationID)) {
                            wMaterialList = wStationMaterialListMap.get(wSFCTaskSelf.StationID);
                        }

                        wSFCTaskSelf.MaterialList = SFC_GetMaterialList(wLoginUser,
                                wRoutePartPointIDBomListMap.get(wSFCTaskSelf.RoutePartPointID),
                                wMaterialList, wErrorCode);
                    }
                }
            }

            if (wResult.Result.size() <= 0) {
                wResult.FaultCode += StringUtils.Format("未获取到工单，请检查工位【{0}】是否存在已开工的工单!", wStationCode);
                return wResult;
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 维护产出类型
     */
    private void MaintenanceOutputQuantity(BMSEmployee wLoginUser, List<SFCTaskSelf> wSFCTaskSelfList,
                                           OutResult<Integer> wErrorCode) {
        try {
            if (wSFCTaskSelfList == null || wSFCTaskSelfList.size() <= 0) {
                return;
            }

            //查询产品组成
            Map<String, Object> wParams = new HashMap<String, Object>();
            List<FPCProductCom> fpcProductComs = mFPCProductComService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            Map<Integer, List<FPCProductCom>> fpcProductComsMap = fpcProductComs.stream()
                    .collect(Collectors.groupingBy(FPCProductCom::getProductID));
            //转换为实际的需求数
            for (SFCTaskSelf sfcTaskSelf : wSFCTaskSelfList) {
                if (!fpcProductComsMap.containsKey(sfcTaskSelf.ProductID)) {
                    continue;
                }
                List<FPCProductCom> fpcProductComs1 = fpcProductComsMap.get(sfcTaskSelf.ProductID);
                if (sfcTaskSelf.ComponentType == PDMComponentTypes.Box.getValue()) {
                    Optional<FPCProductCom> first =
                            fpcProductComs1.stream().filter(p -> p.ComponentType == PDMComponentTypes.Product.getValue()
                                    && p.TargetComponentType == PDMComponentTypes.Box.getValue()).findFirst();
                    if (first.isPresent()) {
                        FPCProductCom fpcProductCom = first.get();
                        if (fpcProductCom.ComponentNumber > 0) {
                            sfcTaskSelf.PlantComNum = sfcTaskSelf.PlantNum / fpcProductCom.ComponentNumber;
                        } else {
                            sfcTaskSelf.PlantComNum = sfcTaskSelf.PlantNum;
                        }
                    }
                } else {
                    Optional<FPCProductCom> first =
                            fpcProductComs1.stream().filter(p -> p.ComponentType == sfcTaskSelf.ComponentType
                                    && p.TargetComponentType == PDMComponentTypes.Product.getValue()).findFirst();
                    if (first.isPresent()) {
                        FPCProductCom fpcProductCom = first.get();
                        if (fpcProductCom.ComponentNumber > 0) {
                            sfcTaskSelf.PlantComNum = sfcTaskSelf.PlantNum * fpcProductCom.ComponentNumber;
                        } else {
                            sfcTaskSelf.PlantComNum = sfcTaskSelf.PlantNum;
                        }
                    } else {
                        sfcTaskSelf.PlantComNum = sfcTaskSelf.PlantNum;
                    }
                }
            }
            //计算实际执行工件数
            List<Integer> wTaskSelfIDList = wSFCTaskSelfList.stream().map(p -> p.ID).collect(Collectors.toList());
            wParams.clear();
            wParams.put("TaskSelfID", StringUtils.Join(",", wTaskSelfIDList));
            wParams.put("Status", SFCTaskReportStatus.CONFIRMED.getValue());
            List<SFCTaskReport> sfcTaskReports = mSFCTaskReportService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (sfcTaskReports != null && sfcTaskReports.size() > 0) {
                Map<Integer, List<SFCTaskReport>> wTaskReportMap = sfcTaskReports.stream()
                        .collect(Collectors.groupingBy(p -> p.TaskSelfID));
                for (SFCTaskSelf sfcTaskSelf : wSFCTaskSelfList) {
                    if (wTaskReportMap.containsKey(sfcTaskSelf.ID)) {
                        List<SFCTaskReport> wTaskReportList = wTaskReportMap.get(sfcTaskSelf.ID);
                        sfcTaskSelf.ActComNum = (float) wTaskReportList.stream().mapToDouble(p -> p.ActNum).sum();
                    }
                }
            }
        } catch (Exception ex) {
            log.error("MaintenanceOutputQuantity", ex);
        }
    }

    /**
     * 获取物料清单
     */
    private List<SFCStatusFeedbackItem> SFC_GetMaterialList(BMSEmployee wLoginUser, List<MSSBOMItem> wMSSBOMItemList,
                                                            List<SFCStationMaterial> wMaterialList,
                                                            OutResult<Integer> wErrorCode) {
        List<SFCStatusFeedbackItem> wResult = new ArrayList<SFCStatusFeedbackItem>();
        try {
            if (wMSSBOMItemList == null || wMSSBOMItemList.size() == 0) {
                return wResult;
            }

            Map<Integer, List<SFCStationMaterial>> wRoutePartPointIDMaterialListMap = wMaterialList.stream()
                    .collect(Collectors.groupingBy(SFCStationMaterial::getRoutePartPointID));

            SFCStatusFeedbackItem wSFCStatusFeedbackItem;
            String wBatchNumber = "";
            for (MSSBOMItem wMSSBOMItem : wMSSBOMItemList) {
                if (wRoutePartPointIDMaterialListMap.containsKey(wMSSBOMItem.RoutePartPointID)) {
                    List<SFCStationMaterial> sfcStationMaterials =
                            wRoutePartPointIDMaterialListMap.get(wMSSBOMItem.RoutePartPointID);
                    if (sfcStationMaterials.stream().anyMatch(p -> p.MaterialID == wMSSBOMItem.MaterialID)) {
                        wBatchNumber =
                                sfcStationMaterials.stream().filter(p -> p.MaterialID == wMSSBOMItem.MaterialID).findFirst().get().BatchNumber;
                    }
                }

                wSFCStatusFeedbackItem = new SFCStatusFeedbackItem(String.valueOf(wMSSBOMItem.MaterialID),
                        wMSSBOMItem.MaterialName, wMSSBOMItem.MaterialNo,
                        wMSSBOMItem.MaterialNumber.floatValue(), wBatchNumber);
                wResult.add(wSFCStatusFeedbackItem);
            }
        } catch (Exception e) {
            log.error("SFC_GetMaterialList", e);
        }
        return wResult;
    }

    /**
     * 获取工艺参数数据结构
     */
    private List<SFCParameterItem> SFC_GetParameterList(BMSEmployee wLoginUser,
                                                        List<FPCRoutePartPointParameter> wFPCRoutePartPointParameterList, OutResult<Integer> wErrorCode) {
        List<SFCParameterItem> wResult = new ArrayList<SFCParameterItem>();
        try {
            if (wFPCRoutePartPointParameterList == null || wFPCRoutePartPointParameterList.size() == 0) {
                return wResult;
            }

            SFCParameterItem wSFCParameterItem;
            for (FPCRoutePartPointParameter wFPCRoutePartPointParameter : wFPCRoutePartPointParameterList) {
                wSFCParameterItem = new SFCParameterItem();
                wSFCParameterItem.DataType = GetDataTypeByStandardType(wLoginUser,
                        wFPCRoutePartPointParameter.StandardType, wErrorCode);
                wSFCParameterItem.ParameterCode = wFPCRoutePartPointParameter.CollectionNumber;
                wSFCParameterItem.ParameterName = wFPCRoutePartPointParameter.ParamName;
                wSFCParameterItem.ParameterValue = wFPCRoutePartPointParameter.StandardValue;
                wSFCParameterItem.ParameterValueDown =
                        StringUtils.parseString(wFPCRoutePartPointParameter.StandardLeft);
                wSFCParameterItem.ParameterValueUp = StringUtils.parseString(wFPCRoutePartPointParameter.StandardRight);
                wResult.add(wSFCParameterItem);
            }
        } catch (Exception e) {
            log.error("SFC_GetParameterList", e);
        }
        return wResult;

    }

    /**
     * 获取数据类型
     * * 参数值类型
     * * 0    default
     * * 6    boolean
     * * 7    int
     * * 8    string
     * * 9    float
     * * 10   double
     */
    private int GetDataTypeByStandardType(BMSEmployee wLoginUser, int wStandardType, OutResult<Integer> wErrorCode) {
        int wResult = 0;
        try {
            if (wStandardType == 0 || wStandardType == 11 || wStandardType == 12 || wStandardType == 13 || wStandardType == 14 || wStandardType == 15) {
                wResult = 8;
            } else if (wStandardType == 1) {
                wResult = 6;
            } else if (wStandardType == 16) {
                wResult = 7;
            } else {
                wResult = 9;
            }
        } catch (Exception ex) {
            log.error("GetDataTypeByStandardType", ex);
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_DeleteTaskSelf(BMSEmployee wLoginUser, SFCTaskSelf wSFCTaskSelf) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            if (wSFCTaskSelf.Status >= SFCTaskStatus.Doing.getValue()) {
                wResult.FaultCode += "删除失败：只能删除`保存`或`待开工`的工单";
                return wResult;
            }

            SFCTaskSelfDAO.getInstance().SFC_DeleteTaskSelf(wLoginUser, wSFCTaskSelf, wErrorCode);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    //endregion

    //region 开工打卡

    @Override
    public ServiceResult<List<SFCTaskSelf>> SFC_QueryClockTask(BMSEmployee wLoginUser, Map<String, Object> wParams,
                                                               Pagination wPagination) {
        ServiceResult<List<SFCTaskSelf>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            wParams.put("WorkerID", wLoginUser.ID);
            wResult.Result = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams, wPagination,
                    wErrorCode);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_BatchClock(BMSEmployee wLoginUser, List<SFCTaskSelf> wSFCTaskSelfList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            SFCLoginEvent wSFCLoginEvent;
            SFCTaskIPT wSFCTaskIPT;
            List<SFCTaskSelf> wWorkerSelfList;
            int wEffectRows = 0;
            Map<String, Object> wParams = new HashMap<String, Object>();
            BMSClassRange wBMSClassRange;
            List<BFCMessage> wBFCMessageList = new ArrayList<>();
            SimpleDateFormat wSDF = new SimpleDateFormat("yyyyMMdd");
            int wShiftID = Integer.parseInt(wSDF.format(Calendar.getInstance().getTime()));

            Map<String, Object> wParams1 = new HashMap<String, Object>();
            List<SFCTaskSelf> wClockedTaskList;
            for (SFCTaskSelf wSFCTaskSelf : wSFCTaskSelfList) {

                //判断该工序是否已停序
                if (EXCServiceImpl.getInstance().EXC_IsRoutePartPointStop(wLoginUser, wSFCTaskSelf.OrderID,
                        wSFCTaskSelf.RoutePartPointID).Result) {
                    wResult.FaultCode += StringUtils.Format("开工失败：工序【{0}】已停序!!!", wSFCTaskSelf.RoutePartPointName);
                    return wResult;
                }

                //获取工位信息
                wParams.clear();
                wParams.put("ID", wSFCTaskSelf.StationID);
                wParams.put("Active", 1);
                FMCStation fmcStation = mFMCStationService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (fmcStation == null || fmcStation.ID <= 0) {
                    wResult.FaultCode += StringUtils.Format("开工失败：工位【{0}】未激活!!!", wSFCTaskSelf.StationName);
                    return wResult;
                }

                //判断当前工位、当前操作工是否已有派工任务开工，若有，则提示
                wParams1.clear();
                wParams1.put("StationID", wSFCTaskSelf.StationID);
                wParams1.put("Status", SFCTaskStatus.Doing.getValue());
                wParams1.put("Active", 1);
                wClockedTaskList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams1, Pagination.getNewMaxSize(),
                        wErrorCode);
//                wClockedTaskList.removeIf(p -> p.RoutePartPointID == wSFCTaskSelf.RoutePartPointID);

                //判断工位是否配置了不允许同时开工多个工单
                if (fmcStation.IsMultiStartSupport == 0 && wClockedTaskList.size() > 0) {
                    wResult.FaultCode += StringUtils.Format("开工失败：工位【{0}】已配置不允许同时开工多个工单!!!", wSFCTaskSelf.StationName);
                    return wResult;
                }

                //判断同工位只允许同时开工相同的工序
                if (wClockedTaskList != null && wClockedTaskList.size() > 0
                        && wClockedTaskList.stream().anyMatch(p -> !p.RoutePartPointName.equals(wSFCTaskSelf.RoutePartPointName))) {
                    SFCTaskSelf wClockedTask =
                            wClockedTaskList.stream().filter(p -> !p.RoutePartPointName.equals(wSFCTaskSelf.RoutePartPointName)).findFirst().get();
                    wResult.FaultCode += StringUtils.Format("您已开工【{2}】-【{0}】-【{1}】，请先完工该派工任务!",
                            wClockedTask.StationName, wClockedTask.RoutePartPointName, wClockedTask.OrderNo);
                    return wResult;
                }

                //①新增打卡记录
                wSFCLoginEvent = new SFCLoginEvent();
                wSFCLoginEvent.OrderID = wSFCTaskSelf.OrderID;
                wSFCLoginEvent.OrderNo = wSFCTaskSelf.OrderNo;
                wSFCLoginEvent.RoutePartID = wSFCTaskSelf.RoutePartID;
                wSFCLoginEvent.RoutePartPointID = wSFCTaskSelf.RoutePartPointID;
                wSFCLoginEvent.ProductID = wSFCTaskSelf.ProductID;
                wSFCLoginEvent.TaskPartID = wSFCTaskSelf.TaskPartID;
                wSFCLoginEvent.TaskPartPointID = wSFCTaskSelf.TaskPartPointID;
                wSFCLoginEvent.TaskPartPointDayID = wSFCTaskSelf.TaskPartPointDayID;
                wSFCLoginEvent.WorkShopID = wSFCTaskSelf.WorkShopID;
                wSFCLoginEvent.WorkAreaID = wSFCTaskSelf.WorkAreaID;
                wSFCLoginEvent.StationID = wSFCTaskSelf.StationID;
                wSFCLoginEvent.ShiftType = wSFCTaskSelf.ShiftType;
                wSFCLoginEvent.TaskSelfID = wSFCTaskSelf.ID;
                wSFCLoginEvent.LoginType = SFCLoginType.START.getValue();
                wSFCLoginEvent.CreatorID = wLoginUser.ID;
                wSFCLoginEvent.EditorID = wLoginUser.ID;
                wEffectRows = mSFCLoginEventService.UpdatePo(wLoginUser, wSFCLoginEvent, wErrorCode);
                if (wEffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }

                //修改派工任务状态
                wSFCTaskSelf.EditorID = wLoginUser.ID;
                wSFCTaskSelf.EditTime = Calendar.getInstance();
                wSFCTaskSelf.Status = SFCTaskStatus.Doing.getValue();
                mSFCTaskSelfService.UpdatePo(wLoginUser, wSFCTaskSelf, wErrorCode);

                //完工，历史工单（相同订单、相同工序、相同班次的工单）
                SFCFrockBindServiceImpl.getInstance().SFC_FinishSameSelfTask(wLoginUser, wSFCTaskSelf);

                //修改日计划状态为`开工`
                wParams1.clear();
                wParams1.put("ID", wSFCTaskSelf.TaskPartPointDayID);
                APSTaskPartPointDay wAPSTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams1,
                        wErrorCode);
                if (wAPSTaskPartPointDay.FirstCheckStatus != APSFirstCheckStatus.Default.getValue()) {
                    wAPSTaskPartPointDay.FirstCheckStatus = APSFirstCheckStatus.ToCheck.getValue();
                }
                if (wAPSTaskPartPointDay != null && wAPSTaskPartPointDay.ID > 0 && wAPSTaskPartPointDay.Status == APSTaskStatus.Confirm.getValue()) {
                    wAPSTaskPartPointDay.Status = APSTaskStatus.Started.getValue();
                    wAPSTaskPartPointDay.setUserInfo(wLoginUser);
                }
                mAPSTaskPartPointDayService.UpdatePo(wLoginUser, wAPSTaskPartPointDay, wErrorCode);
                //修改工序计划状态为`开工`
                wParams1.clear();
                wParams1.put("ID", wSFCTaskSelf.TaskPartPointID);
                APSTaskPartPoint wAPSTaskPartPoint = mAPSTaskPartPointService.SelectPO(wLoginUser, wParams1,
                        wErrorCode);
                if (wAPSTaskPartPoint != null && wAPSTaskPartPoint.ID > 0 && wAPSTaskPartPoint.Status == APSTaskStatus.Issued.getValue()) {
                    wAPSTaskPartPoint.Status = APSTaskStatus.Started.getValue();
                    wAPSTaskPartPoint.setUserInfo(wLoginUser);
                    mAPSTaskPartPointService.UpdatePo(wLoginUser, wAPSTaskPartPoint, wErrorCode);
                }
                //修改周计划状态为`开工`
                wParams1.clear();
                wParams1.put("ID", wSFCTaskSelf.TaskPartID);
                APSTaskPart wAPSTaskPart = mAPSTaskPartService.SelectPO(wLoginUser, wParams1,
                        wErrorCode);
                if (wAPSTaskPart != null && wAPSTaskPart.ID > 0 && wAPSTaskPart.Status == APSTaskStatus.Issued.getValue()) {
                    wAPSTaskPart.Status = APSTaskStatus.Started.getValue();
                    wAPSTaskPart.setUserInfo(wLoginUser);
                    mAPSTaskPartService.UpdatePo(wLoginUser, wAPSTaskPart, wErrorCode);
                }
                //修改订单状态为`开工`
                wParams1.clear();
                wParams1.put("ID", wSFCTaskSelf.OrderID);
                OMSOrder wOMSOrder = mOMSOrderService.SelectPO(wLoginUser, wParams1, wErrorCode);
                if (wOMSOrder != null && wOMSOrder.ID > 0 && wOMSOrder.Status == OMSOrderStatus.PlanReady.getValue()) {
                    wOMSOrder.setUserInfo(wLoginUser);
                    wOMSOrder.Status = OMSOrderStatus.Producing.getValue();
                    wOMSOrder.RealStartDate = Calendar.getInstance();
                    mOMSOrderService.UpdatePo(wLoginUser, wOMSOrder, wErrorCode);
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_BatchClock" + e);
        }
        return wResult;
    }

    /**
     * 添加返修记录
     */
    private void AddEXCProductRepairRecordItem(BMSEmployee wLoginUser, SFCTaskSelf wSFCTaskSelf, SFCTaskIPT wSFCTaskIPT,
                                               SFCWorkpieceRecord wSFCWorkpieceRecord, OutResult<Integer> wErrorCode) {
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCTaskSelf.TaskPartPointDayID);
            APSTaskPartPointDay wAPSTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser,
                    wParams,
                    wErrorCode);

            if (wAPSTaskPartPointDay == null || wAPSTaskPartPointDay.ID <= 0 || wAPSTaskPartPointDay.ProductRepairID <= 0) {
                return;
            }
            //①查询返修单
            wParams.clear();
            wParams.put("ID", wAPSTaskPartPointDay.ProductRepairID);
            EXCProductRepair wEXCProductRepair = mEXCProductRepairService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wEXCProductRepair == null || wEXCProductRepair.ID <= 0) {
                log.error("AddEXCProductRepairRecordItem:返修单查询失败,ID=" + wAPSTaskPartPointDay.ProductRepairID);
                return;
            }
            //②创建返修记录
            Calendar wBaseTime = Calendar.getInstance();
            wBaseTime.set(2000, 0, 1, 0, 0, 0);

            EXCProductRepairItem wEXCProductRepairItem = new EXCProductRepairItem();
            wEXCProductRepairItem.TaskID = wAPSTaskPartPointDay.ProductRepairID;
            wEXCProductRepairItem.TaskCode = wEXCProductRepair.Code;
            wEXCProductRepairItem.WorkpieceID = wSFCWorkpieceRecord.ID;
            wEXCProductRepairItem.WorkpieceNo = wSFCWorkpieceRecord.Code;
            wEXCProductRepairItem.OrderID = wSFCTaskSelf.OrderID;
            wEXCProductRepairItem.OrderNo = wSFCTaskSelf.OrderNo;
            wEXCProductRepairItem.ProductID = wSFCTaskSelf.ProductID;
            wEXCProductRepairItem.Type = wEXCProductRepair.Type;
            wEXCProductRepairItem.RoutePartPointID = wSFCTaskSelf.RoutePartPointID;
            wEXCProductRepairItem.RoutePartPointName = wSFCTaskSelf.RoutePartPointName;
            wEXCProductRepairItem.RoutePartPointCode = wSFCTaskSelf.RoutePartPointCode;
            wEXCProductRepairItem.RoutePartID = wSFCTaskSelf.RoutePartID;
            wEXCProductRepairItem.RoutePartName = wSFCTaskSelf.RoutePartName;
            wEXCProductRepairItem.RoutePartCode = wSFCTaskSelf.RoutePartCode;
            wEXCProductRepairItem.WorkID = wLoginUser.ID;
            wEXCProductRepairItem.CheckTime = wBaseTime;
            wEXCProductRepairItem.ReportTime = wBaseTime;
            wEXCProductRepairItem.Status = EXCProductRepairItemStatus.InProgress.getValue();
            mEXCProductRepairItemService.UpdatePo(wLoginUser, wEXCProductRepairItem, wErrorCode);
        } catch (Exception ex) {
            log.error("AddEXCProductRepairRecordItem", ex);
        }
    }

    //endregion

    //region 检验

    @Override
    public ServiceResult<List<SFCTaskIPT>> SFC_QueryCheckTask(BMSEmployee wLoginUser, BaseTagType wBaseTagType,
                                                              Calendar wStartTime, Calendar wEndTime, Map<String,
            Object> wParams, Pagination wPagination) {
        ServiceResult<List<SFCTaskIPT>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            switch (wBaseTagType) {
                case ToDo:
                    wParams.put("Status", SFCTaskStatus.Doing.getValue());
                    wParams.put("WorkerIDList", wLoginUser.ID);
                    wResult.Result = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
                    break;
                case Done:
                    wParams.put("Status", SFCTaskStatus.Done.getValue());
                    wParams.put("WorkerIDList", wLoginUser.ID);
                    wParams.put("CStartTime", wStartTime);
                    wParams.put("CEndTime", wEndTime);
                    wResult.Result = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
                    break;
                default:
                    break;
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> IPT_QueryDetailsByTask(BMSEmployee wLoginUser, int wTaskID) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wTaskID);
            SFCTaskIPT wSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wSFCTaskIPT == null || wSFCTaskIPT.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskIPT.RoutePartPointID);
            wParams.put("Active", 1);
            List<FPCRouteStep> wFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            if (wFPCRouteStepList == null || wFPCRouteStepList.size() == 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //填写值
            wParams.clear();
            wParams.put("TaskID", wSFCTaskIPT.ID);
            List<IPTValue> wIPTValueList = mIPTValueService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);

            //待办
            List<FPCRouteStep> wToDoList =
                    wFPCRouteStepList.stream().filter(p -> wIPTValueList.stream().noneMatch(q -> q.RouteStepID == p.ID)).collect(Collectors.toList());
            //已办
            List<FPCRouteStep> wDoneList =
                    wFPCRouteStepList.stream().filter(p -> wIPTValueList.stream().anyMatch(q -> q.RouteStepID == p.ID)).collect(Collectors.toList());

            wResult.CustomResult.put("ToDoList", wToDoList);
            wResult.CustomResult.put("DoneList", wDoneList);
            wResult.CustomResult.put("IPTValueList", wIPTValueList);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> IPT_SubmitValue(BMSEmployee wLoginUser, List<IPTValue> wIPTValueList,
                                                  boolean wIsDevice, boolean wIsSave) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            long startTime = System.currentTimeMillis();

            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //验证订单状态，若处于`暂停`状态，不允许作业
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wIPTValueList.get(0).TaskID);
            SFCTaskIPT wCheckTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
            wResult.CustomResult.put("SFCTaskIPT", wCheckTaskIPT);
            if (wCheckTaskIPT != null && wCheckTaskIPT.ID > 0) {
                wParams.clear();
                wParams.put("ID", wCheckTaskIPT.OrderID);
                OMSOrder wCheckOrder = mOMSOrderService.SelectPO(wLoginUser, wParams, wErrorCode);
                if (wCheckOrder.Status == OMSOrderStatus.Suspend.getValue()) {
                    wResult.FaultCode += StringUtils.Format("提交失败：订单【{0}】已处于`暂停`状态，不允许作业!", wCheckOrder.Code);
                    return wResult;
                }
            }

            //判断该工序是否已停序
            if (EXCServiceImpl.getInstance().EXC_IsRoutePartPointStop(wLoginUser, wCheckTaskIPT.OrderID,
                    wCheckTaskIPT.RoutePartPointID).Result) {
                wResult.FaultCode += StringUtils.Format("操作失败：工序【{0}】已停序!!!", wCheckTaskIPT.RoutePartPointName);
                return wResult;
            }

            for (IPTValue wIPTValue : wIPTValueList) {
                if (!wIsDevice) {
                    wIPTValue.setUserInfo(wLoginUser);
                }
            }
//            mIPTValueService.UpdatePoList(wLoginUser, wIPTValueList, wErrorCode);
            //批量保存检验值
            SFCFrockBindServiceImpl.getInstance().IPT_UpdateValueList(wLoginUser, wIPTValueList);

            // 若为保存操作，则返回，不执行后续逻辑
            if (wIsSave) {
                return wResult;
            }

            //完成检验任务
            List<Integer> wTaskIDList =
                    wIPTValueList.stream().map(p -> p.TaskID).distinct().collect(Collectors.toList());
            wParams.clear();
            List<SFCTaskIPT> sfcTaskIPTS = mSFCTaskIPTService.SelectPOList(wLoginUser, wTaskIDList, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);

            //若为设备自动自检，需判断首检
            if (wIsDevice) {
                for (SFCTaskIPT sfcTaskIPT : sfcTaskIPTS) {
                    wParams.clear();
                    wParams.put("ID", sfcTaskIPT.TaskPartPointDayID);
                    APSTaskPartPointDay apsTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (apsTaskPartPointDay == null || apsTaskPartPointDay.ID <= 0) {
                        continue;
                    }

                    if (apsTaskPartPointDay.FirstCheckStatus == APSFirstCheckStatus.ToCheck.getValue()
                            || apsTaskPartPointDay.FirstCheckStatus == APSFirstCheckStatus.Locked.getValue()
                            || apsTaskPartPointDay.FirstCheckStatus == APSFirstCheckStatus.CheckBad.getValue()) {
                        wResult.FaultCode += StringUtils.Format("工序【{0}】首检未完成!",
                                sfcTaskIPT.RoutePartPointName);
                        return wResult;
                    }
                }
            }

            //查询工件记录
            String wComponentCodes = StringUtils.Join("','",
                    sfcTaskIPTS.stream().map(p -> p.ComponentCode).distinct().collect(Collectors.toList()));
            List<Integer> wRecordIDList = SFCWorkpieceRecordDAO.getInstance().SFC_GetRecordIDList(wLoginUser,
                    wComponentCodes, wErrorCode);
            wParams.clear();
            List<SFCWorkpieceRecord> sfcWorkpieceRecords = mSFCWorkpieceRecordService.SelectPOList(wLoginUser,
                    wRecordIDList, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);

            for (SFCTaskIPT sfcTaskIPT : sfcTaskIPTS) {
                Optional<SFCWorkpieceRecord> first =
                        sfcWorkpieceRecords.stream().filter(p -> p.Code.equals(sfcTaskIPT.ComponentCode)).findFirst();
                if (!first.isPresent()) {
                    continue;
                }

                SFCWorkpieceRecord wSFCWorkpieceRecord = first.get();

                sfcTaskIPT.setUserInfo(wLoginUser);
                sfcTaskIPT.Status = SFCTaskStatus.Done.getValue();

                //更新追溯信息
                TaskTimerUtils.AddTask("TriggerUpdataLogic",
                        () -> TriggerUpdataLogic(wLoginUser, wIPTValueList, wResult,
                                wErrorCode, wParams, sfcTaskIPT, sfcTaskIPT.ID, wSFCWorkpieceRecord), 1000
                );
            }

            mSFCTaskIPTService.UpdatePoList(wLoginUser, sfcTaskIPTS, wErrorCode);

            //更新首检信息（若还有没提交的首检单，那么此次提交无效），若首检单全部提交了，则一把更新
            if (sfcTaskIPTS.stream().anyMatch(p -> p.FirstFlag == 1)) {
                SFCFrockBindServiceImpl.getInstance().SFC_SubmitDayPlanFirstCheckStatus(wLoginUser,
                        sfcTaskIPTS);
            }

            //自动报工逻辑触发
            if (wCheckTaskIPT.Type == FPCCheckType.SELF_CHECK.getValue()) {
                TaskTimerUtils.AddTask("SFC_AutoReport",
                        () -> SFCServiceImpl.getInstance().SFC_AutoReport(wLoginUser, wCheckTaskIPT)
                        , 1000);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());

            long endTime = System.currentTimeMillis();
            log.info("IPT_SubmitValue： " + (endTime - startTime) + "ms");
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Nullable
    private ServiceResult<Integer> TriggerUpdataLogic(BMSEmployee wLoginUser, List<IPTValue> wIPTValueList,
                                                      ServiceResult<Integer> wResult, OutResult<Integer> wErrorCode,
                                                      Map<String, Object> wParams,
                                                      SFCTaskIPT wSFCTaskIPT, int wTaskID,
                                                      SFCWorkpieceRecord wSFCWorkpieceRecord) {
        try {
            int wEffectRows;
            //更新工件下线工位和状态信息
            wSFCWorkpieceRecord.setUserInfo(wLoginUser);
            wSFCWorkpieceRecord.OffLineTime = Calendar.getInstance();
            wSFCWorkpieceRecord.OffLineStationID = wSFCTaskIPT.StationID;
            wSFCWorkpieceRecord.Status = SFCTaskStatus.Doing.getValue();
            wSFCWorkpieceRecord.CheckResult = wSFCTaskIPT.Result;
            wEffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
            if (wEffectRows <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            //产品码绑定元件/芯子、外壳码绑定产品码、装箱码绑定外壳码
            SFCServiceImpl.getInstance().SFC_BindingNo(wLoginUser, wSFCTaskIPT, wIPTValueList);

            //自检作业时，创建上道工序的互检单，并维护互检信息
            if (wSFCTaskIPT.Type == FPCCheckType.SELF_CHECK.getValue()) {
                CreateMutualIPTTask(wLoginUser, wSFCTaskIPT, wIPTValueList, wSFCWorkpieceRecord);
            }

            //维护工件的履历（自检信息）
            if (wSFCTaskIPT.Type == FPCCheckType.SELF_CHECK.getValue()) {
                wParams.clear();
                wParams.put("WorkpieceRecordID", wSFCWorkpieceRecord.ID);
                wParams.put("StationID", wSFCTaskIPT.StationID);
                wParams.put("RoutePartPointID", wSFCTaskIPT.RoutePartPointID);
                SFCWorkpieceResume wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser,
                        wParams,
                        wErrorCode);
                if (wSFCWorkpieceResume != null && wSFCWorkpieceResume.ID > 0) {
                    wSFCWorkpieceResume.setUserInfo(wLoginUser);
                    wSFCWorkpieceResume.SelfTaskIPTID = wSFCTaskIPT.ID;
                    wSFCWorkpieceResume.SelfCheckID = wLoginUser.ID;
                    wSFCWorkpieceResume.SelfCheckTime = Calendar.getInstance();
                    wSFCWorkpieceResume.CheckResult = wSFCTaskIPT.Result;
                    wSFCWorkpieceResume.BadReason = wSFCTaskIPT.Description;
                    wEffectRows = mSFCWorkpieceResumeService.UpdatePo(wLoginUser, wSFCWorkpieceResume,
                            wErrorCode);
                    if (wEffectRows <= 0) {
                        wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                        return wResult;
                    }
                }
            }
            //维护工件上工序的履历（互检信息）
            int wThisRoutePartPointID = wSFCTaskIPT.RoutePartPointID;
            if (wIPTValueList.stream().anyMatch(p -> p.TaskID == wTaskID && p.RoutePartPointID != wThisRoutePartPointID)) {
                int wLastRoutePartPointID =
                        wIPTValueList.stream().filter(p -> p.TaskID == wTaskID
                                && p.RoutePartPointID != wThisRoutePartPointID).findFirst().get().RoutePartPointID;

                wParams.clear();
                wParams.put("WorkpieceRecordID", wSFCWorkpieceRecord.ID);
                wParams.put("RoutePartPointID", wLastRoutePartPointID);
                SFCWorkpieceResume wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser,
                        wParams,
                        wErrorCode);
                if (wSFCWorkpieceResume != null && wSFCWorkpieceResume.ID > 0) {
                    wSFCWorkpieceResume.setUserInfo(wLoginUser);
                    wSFCWorkpieceResume.MutualCheckID = wLoginUser.ID;
                    wSFCWorkpieceResume.MutualCheckTime = Calendar.getInstance();
                    wSFCWorkpieceResume.CheckResult = wSFCTaskIPT.Result;
                    wSFCWorkpieceResume.BadReason = wSFCTaskIPT.Description;
                    wEffectRows = mSFCWorkpieceResumeService.UpdatePo(wLoginUser, wSFCWorkpieceResume,
                            wErrorCode);
                    if (wEffectRows <= 0) {
                        wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                        return wResult;
                    }
                }
            }
            //维护工件的履历（专检信息）
            if (wSFCTaskIPT.Type == FPCCheckType.SPECIAL_CHECK.getValue()) {
                wParams.clear();
                wParams.put("WorkpieceRecordID", wSFCWorkpieceRecord.ID);
                wParams.put("StationID", wSFCTaskIPT.StationID);
                wParams.put("RoutePartPointID", wSFCTaskIPT.RoutePartPointID);
                SFCWorkpieceResume wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser,
                        wParams,
                        wErrorCode);
                if (wSFCWorkpieceResume != null && wSFCWorkpieceResume.ID > 0) {
                    wSFCWorkpieceResume.setUserInfo(wLoginUser);
                    wSFCWorkpieceResume.SpecialTaskIPTID = wSFCTaskIPT.ID;
                    wSFCWorkpieceResume.SpecialCheckID = wLoginUser.ID;
                    wSFCWorkpieceResume.SpecialCheckTime = Calendar.getInstance();
                    wSFCWorkpieceResume.CheckResult = wSFCTaskIPT.Result;
                    wSFCWorkpieceResume.BadReason = wSFCTaskIPT.Description;
                    wEffectRows = mSFCWorkpieceResumeService.UpdatePo(wLoginUser, wSFCWorkpieceResume,
                            wErrorCode);
                    if (wEffectRows <= 0) {
                        wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                        return wResult;
                    }
                }
            }
            //工单绑定物料批次逻辑
            if (wSFCTaskIPT.Type == FPCCheckType.SELF_CHECK.getValue()) {
                TaskIPTBindMaterialBatch(wLoginUser, wSFCTaskIPT, wErrorCode);
            }

            //自检时，若为返修计划，维护返修记录的状态
            if (wSFCTaskIPT.Type == FPCCheckType.SELF_CHECK.getValue()) {
                TriggerProductRepairItemStatusUpdateLogic(wLoginUser, wSFCTaskIPT, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("TriggerUpdataLogic", ex);
        }
        return null;
    }

    /**
     * 返修记录状态维护
     */
    private void TriggerProductRepairItemStatusUpdateLogic(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT,
                                                           OutResult<Integer> wErrorCode) {
        try {
            TaskTimerUtils.AddTask("TriggerProductRepairItemStatusUpdateLogic",
                    () -> EXCServiceImpl.getInstance().EXC_ProductRepairItemStatusUpdateLogic(wLoginUser, wSFCTaskIPT
                            , false)
            );
        } catch (Exception ex) {
            log.error("TriggerProductRepairItemStatusUpdateLogic", ex);
        }
    }

    /**
     * 自检作业时，创建上道工序的互检单，并维护互检信息
     */
    private void CreateMutualIPTTask(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT, List<IPTValue> wIPTValueList,
                                     SFCWorkpieceRecord wSFCWorkpieceRecord) {
        try {
            List<IPTValue> wValueList =
                    wIPTValueList.stream().filter(p -> p.TaskID == wSFCTaskIPT.ID).collect(Collectors.toList());
            List<Integer> wRoutePartPointIDList =
                    wValueList.stream().map(p -> p.RoutePartPointID).distinct().collect(Collectors.toList());
            if (wRoutePartPointIDList == null || wRoutePartPointIDList.size() <= 1) {
                return;
            }

            int wPrevRoutePartPointID =
                    wRoutePartPointIDList.stream().filter(p -> p != wSFCTaskIPT.RoutePartPointID).findFirst().get();

            OutResult<Integer> wErrorCode = new OutResult<Integer>(0);
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("OrderID", wSFCTaskIPT.OrderID);
            wParams.put("RoutePartPointID", wPrevRoutePartPointID);
            wParams.put("ComponentCode", wSFCTaskIPT.ComponentCode);
            wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
            SFCTaskIPT wPreveTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wPreveTaskIPT == null || wPreveTaskIPT.ID <= 0) {
                return;
            }
            wPreveTaskIPT.ID = 0;
            wPreveTaskIPT.Code = "";
            wPreveTaskIPT.Type = FPCCheckType.MUTUAL_CHECK.getValue();
            wPreveTaskIPT.IsReported = 0;
            wPreveTaskIPT.TaskReportID = 0;
            wPreveTaskIPT.setUserInfo(wLoginUser);
            int wAffectRows = mSFCTaskIPTService.UpdatePo(wLoginUser, wPreveTaskIPT, wErrorCode);
            if (wAffectRows > 0) {
                List<IPTValue> wPrevValueList =
                        wIPTValueList.stream().filter(p -> p.RoutePartPointID == wPrevRoutePartPointID).collect(Collectors.toList());
                for (IPTValue wIPTValue : wPrevValueList) {
                    wIPTValue.TaskID = wPreveTaskIPT.ID;
                    wIPTValue.setUserInfo(wLoginUser);
                    wAffectRows = mIPTValueService.UpdatePo(wLoginUser, wIPTValue, wErrorCode);
                    if (wAffectRows <= 0) {
                        return;
                    }
                }
                //维护上道工序的工件互检信息
                wParams.clear();
                wParams.put("WorkpieceRecordID", wSFCWorkpieceRecord.ID);
                wParams.put("StationID", wPreveTaskIPT.StationID);
                wParams.put("RoutePartPointID", wPreveTaskIPT.RoutePartPointID);
                SFCWorkpieceResume wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser,
                        wParams,
                        wErrorCode);
                if (wSFCWorkpieceResume != null && wSFCWorkpieceResume.ID > 0) {
                    wSFCWorkpieceResume.setUserInfo(wLoginUser);
                    wSFCWorkpieceResume.MutualTaskIPTID = wPreveTaskIPT.ID;
                    wSFCWorkpieceResume.MutualCheckID = wLoginUser.ID;
                    wSFCWorkpieceResume.MutualCheckTime = Calendar.getInstance();
                    wSFCWorkpieceResume.CheckResult = wSFCTaskIPT.Result;
                    wSFCWorkpieceResume.BadReason = wSFCTaskIPT.Description;
                    mSFCWorkpieceResumeService.UpdatePo(wLoginUser, wSFCWorkpieceResume,
                            wErrorCode);
                }
            }
        } catch (Exception ex) {
            log.error("getDBSQL", ex);
        }
    }

    /**
     * 检验单绑定物料、环境参数
     */
    private void TaskIPTBindMaterialBatch(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT,
                                          OutResult<Integer> wErrorCode) {
        try {
            //①查询物料批次
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("StationID", wSFCTaskIPT.StationID);
            wParams.put("RoutePartPointID", wSFCTaskIPT.RoutePartPointID);
            List<SFCStationMaterial> wSFCStationMaterialList = mSFCStationMaterialService.SelectPOList(wLoginUser,
                    wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCStationMaterialList == null || wSFCStationMaterialList.size() == 0) {
                return;
            }
            //②工单绑定物料
            SFCMaterialLoadingRecord wSFCMaterialLoadingRecord;
            for (SFCStationMaterial wSFCStationMaterial : wSFCStationMaterialList) {
                wSFCMaterialLoadingRecord = new SFCMaterialLoadingRecord(wSFCTaskIPT.ID,
                        wSFCStationMaterial.MaterialID, wSFCStationMaterial.BatchNumber);
                wSFCMaterialLoadingRecord.RawMaterialLot = wSFCStationMaterial.RawMaterialLot;
                wSFCMaterialLoadingRecord.setUserInfo(wLoginUser);
                mSFCMaterialLoadingRecordService.UpdatePo(wLoginUser, wSFCMaterialLoadingRecord, wErrorCode);
            }
            //查询工位实时环境参数
            wParams.clear();
            wParams.put("StationID", wSFCTaskIPT.StationID);
            SFCStationEnvironment sfcStationEnvironment = mSFCStationEnvironmentService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (sfcStationEnvironment != null && sfcStationEnvironment.ID > 0) {
                //绑定环境参数
                SFCIPTEnvironmentRecord wSFCIPTEnvironmentRecord = new SFCIPTEnvironmentRecord(wSFCTaskIPT.ID,
                        sfcStationEnvironment.Temperature
                        , sfcStationEnvironment.Humidity, sfcStationEnvironment.Cleanliness);
                wSFCIPTEnvironmentRecord.setUserInfo(wLoginUser);
                mSFCIPTEnvironmentRecordService.UpdatePo(wLoginUser, wSFCIPTEnvironmentRecord, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("TaskIPTBindMaterialBatch", ex);
        }
    }

    @Override
    public ServiceResult<List<FPCRouteStep>> SFC_QueryItemsByStation(BMSEmployee wLoginUser, int wStationID,
                                                                     int wCheckType) {
        ServiceResult<List<FPCRouteStep>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            //①查询当前工位、当前操作工已开工的派工任务
            wParams.put("StationID", wStationID);
            wParams.put("WorkerID", wLoginUser.ID);
            wParams.put("Status", SFCTaskStatus.Doing.getValue());
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID == 0) {
                wResult.FaultCode += StringUtils.Format("【{0}】在当前工位无已开工的派工任务!", wLoginUser.Name);
                return wResult;
            }
            wResult.CustomResult.put("SFCTaskSelf", wSFCTaskSelf);
            //②根据派工任务查询工步列表（根据检验类型）
            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            if (wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                wParams.put("IsSelfCheck", 1);
            } else if (wCheckType == FPCCheckType.MUTUAL_CHECK.getValue()) {
                wParams.put("IsMutualCheck", 1);
            } else if (wCheckType == FPCCheckType.SPECIAL_CHECK.getValue()) {
                wParams.put("IsSpecialCheck", 1);
            }
            List<FPCRouteStep> wFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            //④查询首检项
            if (wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                if (wSFCTaskSelf.IsFirstChecked != 1) {
                    wParams.clear();
                    wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                    wParams.put("Active", 1);
                    wParams.put("IsFirstCheck", 1);
                    List<FPCRouteStep> wFirstItemList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                            Pagination.getNewMaxSize(), wErrorCode);
                    for (FPCRouteStep wFPCRouteStep : wFirstItemList) {
                        if (wFPCRouteStepList.stream().noneMatch(p -> p.ID == wFPCRouteStep.ID)) {
                            wFPCRouteStepList.add(wFPCRouteStep);
                        }
                    }
                }
            }
            //③查询上工艺工序
            List<FPCRouteStep> wPrevRouteStepList = new ArrayList<>();
            if (wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                wParams.clear();
                wParams.put("ID", wSFCTaskSelf.RoutePartPointID);
                FPCRoutePartPoint wThisRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wThisRoutePartPoint != null && wThisRoutePartPoint.ID > 0 && wThisRoutePartPoint.PrevPartPointID > 0) {
                    wParams.clear();
                    wParams.put("ID", wThisRoutePartPoint.PrevPartPointID);
                    FPCRoutePartPoint wPrevRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (wPrevRoutePartPoint != null && wPrevRoutePartPoint.ID > 0) {
                        //④若上工艺工序的互检模式为本工序互检，则不查询上工序的互检工步
                        //⑤若上工艺工序的互检模式为下工序互检，则查询上工序的互检工步
                        if (wPrevRoutePartPoint.MutualModel == 1) {
                            wParams.clear();
                            wParams.put("RoutePartPointID", wPrevRoutePartPoint.ID);
                            wParams.put("Active", 1);
                            wParams.put("IsMutualCheck", 1);
                            wPrevRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                                    Pagination.getNewMaxSize(), wErrorCode);
                        }
                    }
                }
            }

            // 排序、返回结果
            wPrevRouteStepList.sort(Comparator.comparing(FPCRouteStep::getStepOrder));
            wFPCRouteStepList.sort(Comparator.comparing(FPCRouteStep::getStepOrder));
            wResult.Result.addAll(wPrevRouteStepList);
            wResult.Result.addAll(wFPCRouteStepList);
            //根据派工任务、检验类型查询检验单
            wParams.clear();
            wParams.put("TaskSelfID", wSFCTaskSelf.ID);
            wParams.put("Type", wCheckType);
            wParams.put("Status", SFCTaskStatus.Default.getValue());
            List<SFCTaskIPT> wSFCTaskIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            //根据检验单查询检验值
            List<IPTValue> wValueList = new ArrayList<>();
            if (wSFCTaskIPTList.size() > 0) {
                wParams.clear();
                wParams.put("TaskID", StringUtils.Join(",",
                        wSFCTaskIPTList.stream().map(p -> p.ID).distinct().collect(Collectors.toList())));
                wValueList = mIPTValueService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(), wErrorCode);
            }
            //返回前端
            wResult.CustomResult.put("SFCTaskIPTList", wSFCTaskIPTList);
            wResult.CustomResult.put("ValueList", wValueList);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<SFCTaskIPT> SFC_CreateTaskByWorkpieceNo(BMSEmployee wLoginUser, int wStationID,
                                                                 int wCheckType, String wWorkpieceNo) {
        ServiceResult<SFCTaskIPT> wResult = new ServiceResult<>();
        wResult.Result = new SFCTaskIPT();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            //①查询当前工位、当前操作工已开工的派工任务
            wParams.put("StationID", wStationID);
            wParams.put("WorkerID", wLoginUser.ID);
            wParams.put("Status", SFCTaskStatus.Doing.getValue());
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID == 0) {
                wResult.FaultCode += "当前工位无已开工的派工任务!";
                return wResult;
            }

            //验证工位工序实时物料是否满足
            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            wParams.put("IsCodeScanning", 1);
            List<MSSBOMItem> wMSSBOMItemList = mMSSBOMItemService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            if (wMSSBOMItemList != null && wMSSBOMItemList.size() > 0) {
                wParams.clear();
                wParams.put("StationID", wStationID);
                wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                List<SFCStationMaterial> wSFCStationMaterialList = mSFCStationMaterialService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(), wErrorCode);
                if (wMSSBOMItemList.stream().anyMatch(p -> wSFCStationMaterialList.stream().noneMatch(q -> q.MaterialID == p.MaterialID))) {
                    MSSBOMItem wMSSBOMItem =
                            wMSSBOMItemList.stream().filter(p -> wSFCStationMaterialList.stream().noneMatch(q -> q.MaterialID == p.MaterialID)).findFirst().get();
                    wResult.FaultCode += StringUtils.Format("未查询到【{0}-{1}】物料批次，请及时上料!",
                            wMSSBOMItem.MaterialNo, wMSSBOMItem.MaterialName);
                    return wResult;
                }
            }

            //验证该工件的前工序是否已做自检任务，若没有，则报错提示
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.RoutePartPointID);
            FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            if (wFPCRoutePartPoint.PrevPartPointID > 0) {
                //前工艺工序
                wParams.clear();
                wParams.put("ID", wFPCRoutePartPoint.PrevPartPointID);
                FPCRoutePartPoint wPrevRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wPrevRoutePartPoint == null || wPrevRoutePartPoint.ID <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }

                //前工序自检单
                wParams.clear();
                wParams.put("OrderID", wSFCTaskSelf.OrderID);
                wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                wParams.put("ComponentCode", wWorkpieceNo);
                wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                wParams.put("Status", SFCTaskStatus.Done.getValue());
                SFCTaskIPT wPrevSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
                if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                        && (wPrevSFCTaskIPT == null || wPrevSFCTaskIPT.ID <= 0)) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的自检!", wWorkpieceNo,
                            wFPCRoutePartPoint.PrevPartPointName);
                    return wResult;
                }
                //验证该工件的前工序是否报工
                if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                        && wPrevSFCTaskIPT.IsReported != 1) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的报工或报工确认!", wWorkpieceNo,
                            wFPCRoutePartPoint.PrevPartPointName);
                    return wResult;
                }
                //验证该工件的前工序是否设置的强制做专检，且该工件在前工序已做专检，否则报错提示
                if (wPrevRoutePartPoint.IsSpecialCheck == 1) {
                    wParams.clear();
                    wParams.put("OrderID", wSFCTaskSelf.OrderID);
                    wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                    wParams.put("ComponentCode", wWorkpieceNo);
                    wParams.put("Type", FPCCheckType.SPECIAL_CHECK.getValue());
                    wParams.put("Status", SFCTaskStatus.Done.getValue());
                    SFCTaskIPT wPrevSpecialTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
                    if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                            && (wPrevSpecialTaskIPT == null || wPrevSpecialTaskIPT.ID <= 0)) {
                        wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的专检!", wWorkpieceNo,
                                wFPCRoutePartPoint.PrevPartPointName);
                        return wResult;
                    }
                }
            }

            //验证元件类型，若是工装，则走工装逻辑
            if (wSFCTaskSelf.ComponentType == PDMComponentTypes.Frock.getValue()) {
                wParams.clear();
                wParams.put("FrockNo", wWorkpieceNo);
                List<SFCWorkpieceRecord> wSFCWorkpieceRecordList = mSFCWorkpieceRecordService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(),
                        wErrorCode);
                if (wSFCWorkpieceRecordList == null || wSFCWorkpieceRecordList.size() == 0) {
                    wResult.FaultCode += StringUtils.Format("工装【{0}】未绑定元件!", wWorkpieceNo);
                    return wResult;
                }

                for (SFCWorkpieceRecord wItemRecord : wSFCWorkpieceRecordList) {
                    //②根据工件号查询工件流转记录
                    wParams.clear();
                    wParams.put("Code", wItemRecord.Code);
                    SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    //③若没有，则创建工件流转记录；若有，则判断是否已绑定至其他订单，若是，则报错提示
                    Calendar wBaseTime = Calendar.getInstance();
                    wBaseTime.set(2000, 0, 1, 0, 0, 0);
                    int wAffectRows;
                    if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID == 0) {
                        wSFCWorkpieceRecord = new SFCWorkpieceRecord();
                        wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                        wSFCWorkpieceRecord.Code = wItemRecord.Code;
                        wSFCWorkpieceRecord.ID = 0;
                        wSFCWorkpieceRecord.OffLineStationID = 0;
                        wSFCWorkpieceRecord.OffLineTime = wBaseTime;
                        wSFCWorkpieceRecord.OnLineStationID = wSFCTaskSelf.StationID;
                        wSFCWorkpieceRecord.OnLineTime = Calendar.getInstance();
                        wSFCWorkpieceRecord.OrderID = wSFCTaskSelf.OrderID;
                        wSFCWorkpieceRecord.OrderNo = wSFCTaskSelf.OrderNo;
                        wSFCWorkpieceRecord.ProductID = wSFCTaskSelf.ProductID;
                        wSFCWorkpieceRecord.ComponentType = wSFCTaskSelf.ComponentType;
                        wSFCWorkpieceRecord.Status = SFCTaskStatus.Todo.getValue();
                        wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
                        if (wAffectRows <= 0) {
                            wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                            return wResult;
                        }
                    } else {
                        if (wSFCWorkpieceRecord.OrderID != wSFCTaskSelf.OrderID) {
                            wResult.FaultCode += StringUtils.Format("【{0}】工件已与订单【{1}】绑定!", wItemRecord.Code,
                                    wSFCWorkpieceRecord.OrderNo);
                            return wResult;
                        }
                    }
                    //④根据工件号查询检验单，若存在，则报错提示；若不存在则创建检验单（自检、互检单）
                    wParams.clear();
                    wParams.put("TaskPartPointDayID", wSFCTaskSelf.TaskPartPointDayID);
                    wParams.put("Type", wCheckType);
                    wParams.put("ComponentCode", wItemRecord.Code);
                    SFCTaskIPT wSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (wSFCTaskIPT.ID > 0) {
                        wResult.FaultCode += StringUtils.Format("【{0}】该工件已创建当前工序任务的{1}单!", wItemRecord.Code,
                                FPCCheckType.getEnumType(wCheckType).getLabel());
                        wResult.Result = wSFCTaskIPT;
                        return wResult;
                    } else {
                        //根据日计划ID查询派工记录
                        wParams.clear();
                        wParams.put("TaskPartPointDayID", wSFCTaskSelf.TaskPartPointDayID);
                        List<SFCTaskSelf> wSFCTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                                Pagination.getNewMaxSize(), wErrorCode);

                        wSFCTaskIPT = new SFCTaskIPT();
                        wSFCTaskIPT.setUserInfo(wLoginUser);
                        wSFCTaskIPT.OrderID = wSFCTaskSelf.OrderID;
                        wSFCTaskIPT.OrderNo = wSFCTaskSelf.OrderNo;
                        wSFCTaskIPT.RoutePartID = wSFCTaskSelf.RoutePartID;
                        wSFCTaskIPT.RoutePartPointID = wSFCTaskSelf.RoutePartPointID;
                        wSFCTaskIPT.ProductID = wSFCTaskSelf.ProductID;
                        wSFCTaskIPT.TaskPartID = wSFCTaskSelf.TaskPartID;
                        wSFCTaskIPT.TaskPartPointID = wSFCTaskSelf.TaskPartPointID;
                        wSFCTaskIPT.TaskPartPointDayID = wSFCTaskSelf.TaskPartPointDayID;
                        wSFCTaskIPT.TaskSelfID = wSFCTaskSelf.ID;
                        wSFCTaskIPT.WorkShopID = wSFCTaskSelf.WorkShopID;
                        wSFCTaskIPT.WorkAreaID = wSFCTaskSelf.WorkAreaID;
                        wSFCTaskIPT.StationID = wSFCTaskSelf.StationID;
                        wSFCTaskIPT.ShiftType = wSFCTaskSelf.ShiftType;
                        wSFCTaskIPT.StationDate = wSFCTaskSelf.StationDate;
                        wSFCTaskIPT.Result = 1;
                        wSFCTaskIPT.WorkerIDList = new ArrayList<>();
                        if (wSFCTaskSelfList.size() > 0) {
                            wSFCTaskIPT.WorkerIDList =
                                    wSFCTaskSelfList.stream().map(p -> p.WorkerID).distinct().collect(Collectors.toList());
                        }
                        wSFCTaskIPT.Status = SFCTaskStatus.Default.getValue();
                        wSFCTaskIPT.Type = wCheckType;
                        wSFCTaskIPT.ComponentCode = wItemRecord.Code;
                        wAffectRows = mSFCTaskIPTService.UpdatePo(wLoginUser, wSFCTaskIPT, wErrorCode);
                        if (wAffectRows <= 0) {
                            wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                            wResult.Result = wSFCTaskIPT;
                            return wResult;
                        }
                    }
                }

                return wResult;
            }

            //②根据工件号查询工件流转记录
            wParams.clear();
            wParams.put("Code", wWorkpieceNo);
            SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            //③若没有，则创建工件流转记录；若有，则判断是否已绑定至其他订单，若是，则报错提示
            Calendar wBaseTime = Calendar.getInstance();
            wBaseTime.set(2000, 0, 1, 0, 0, 0);
            int wAffectRows;
            if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID == 0) {
                wSFCWorkpieceRecord = new SFCWorkpieceRecord();
                wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                wSFCWorkpieceRecord.Code = wWorkpieceNo;
                wSFCWorkpieceRecord.ID = 0;
                wSFCWorkpieceRecord.OffLineStationID = 0;
                wSFCWorkpieceRecord.OffLineTime = wBaseTime;
                wSFCWorkpieceRecord.OnLineStationID = wSFCTaskSelf.StationID;
                wSFCWorkpieceRecord.OnLineTime = Calendar.getInstance();
                wSFCWorkpieceRecord.OrderID = wSFCTaskSelf.OrderID;
                wSFCWorkpieceRecord.OrderNo = wSFCTaskSelf.OrderNo;
                wSFCWorkpieceRecord.ProductID = wSFCTaskSelf.ProductID;
                wSFCWorkpieceRecord.ComponentType = wSFCTaskSelf.ComponentType;
                wSFCWorkpieceRecord.Status = SFCTaskStatus.Todo.getValue();
                wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
            } else {
                if (wSFCWorkpieceRecord.OrderID != wSFCTaskSelf.OrderID) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工件已与订单【{1}】绑定!", wWorkpieceNo,
                            wSFCWorkpieceRecord.OrderNo);
                    return wResult;
                }
            }

            //维护工件的履历（上线信息）
            SFCWorkpieceResume wSFCWorkpieceResume = new SFCWorkpieceResume(wSFCWorkpieceRecord.ID,
                    wSFCTaskSelf.StationID, wSFCTaskSelf.RoutePartPointID, Calendar.getInstance(), wLoginUser.ID, 0);
            wSFCWorkpieceResume.setUserInfo(wLoginUser);
            wAffectRows = mSFCWorkpieceResumeService.UpdatePo(wLoginUser, wSFCWorkpieceResume, wErrorCode);
            if (wAffectRows <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //④根据工件号查询检验单，若存在，则报错提示；若不存在则创建检验单（自检、互检单）
            wParams.clear();
            wParams.put("TaskPartPointDayID", wSFCTaskSelf.TaskPartPointDayID);
            wParams.put("Type", wCheckType);
            wParams.put("ComponentCode", wWorkpieceNo);
            SFCTaskIPT wSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskIPT.ID > 0) {
                wResult.FaultCode += StringUtils.Format("【{0}】该工件已创建当前工序任务的{1}单!", wWorkpieceNo,
                        FPCCheckType.getEnumType(wCheckType).getLabel());
                wResult.Result = wSFCTaskIPT;
                return wResult;
            } else {
                //根据日计划ID查询派工记录
                wParams.clear();
                wParams.put("TaskPartPointDayID", wSFCTaskSelf.TaskPartPointDayID);
                List<SFCTaskSelf> wSFCTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(), wErrorCode);

                wSFCTaskIPT = new SFCTaskIPT();
                wSFCTaskIPT.setUserInfo(wLoginUser);
                wSFCTaskIPT.OrderID = wSFCTaskSelf.OrderID;
                wSFCTaskIPT.OrderNo = wSFCTaskSelf.OrderNo;
                wSFCTaskIPT.RoutePartID = wSFCTaskSelf.RoutePartID;
                wSFCTaskIPT.RoutePartPointID = wSFCTaskSelf.RoutePartPointID;
                wSFCTaskIPT.ProductID = wSFCTaskSelf.ProductID;
                wSFCTaskIPT.TaskPartID = wSFCTaskSelf.TaskPartID;
                wSFCTaskIPT.TaskPartPointID = wSFCTaskSelf.TaskPartPointID;
                wSFCTaskIPT.TaskPartPointDayID = wSFCTaskSelf.TaskPartPointDayID;
                wSFCTaskIPT.TaskSelfID = wSFCTaskSelf.ID;
                wSFCTaskIPT.WorkShopID = wSFCTaskSelf.WorkShopID;
                wSFCTaskIPT.WorkAreaID = wSFCTaskSelf.WorkAreaID;
                wSFCTaskIPT.StationID = wSFCTaskSelf.StationID;
                wSFCTaskIPT.ShiftType = wSFCTaskSelf.ShiftType;
                wSFCTaskIPT.StationDate = wSFCTaskSelf.StationDate;
                wSFCTaskIPT.Result = 1;
                wSFCTaskIPT.WorkerIDList = new ArrayList<>();
                if (wSFCTaskSelfList.size() > 0) {
                    wSFCTaskIPT.WorkerIDList =
                            wSFCTaskSelfList.stream().map(p -> p.WorkerID).distinct().collect(Collectors.toList());
                }
                wSFCTaskIPT.Status = SFCTaskStatus.Default.getValue();
                wSFCTaskIPT.Type = wCheckType;
                wSFCTaskIPT.ComponentCode = wWorkpieceNo;
                wAffectRows = mSFCTaskIPTService.UpdatePo(wLoginUser, wSFCTaskIPT, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    wResult.Result = wSFCTaskIPT;
                    return wResult;
                }
            }
            //⑤返回前端
            wResult.Result = wSFCTaskIPT;

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_AutoReport(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            long startTime = System.currentTimeMillis();


            if (wSFCTaskIPT.TaskReportID > 0) {
                return wResult;
            }

            OutResult<Integer> wErrorCode = new OutResult<>(0);
            if (wSFCTaskIPT.RoutePartPointID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //①查询工艺工序，判断是否是自动报工模式且自动报工数量大于0
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCTaskIPT.RoutePartPointID);
            FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            //②若是，则判断当前操作工的派工任务对应的自检单数量是否达到自动报工数量
            if (wFPCRoutePartPoint.ReportMode == SFCTaskReportMode.AUTO_REPORT.getValue() && wFPCRoutePartPoint.ReportFQTY > 0) {
                wParams.clear();
                wParams.put("TaskSelfID", wSFCTaskIPT.TaskSelfID);
                wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                wParams.put("IsReported", 0);
                wParams.put("Status", SFCTaskStatus.Done.getValue());
                wParams.put("Result", 1);
                List<SFCTaskIPT> wNotReportList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(), wErrorCode);
                if (wNotReportList.size() <= 0 || wNotReportList.size() < wFPCRoutePartPoint.ReportFQTY || wFPCRoutePartPoint.ReportFQTY <= 0) {
                    return wResult;
                }
                //①将待报工的工单依次放入报工集合
                List<List<SFCTaskIPT>> wToReportList = new ArrayList<>();
                List<SFCTaskIPT> wTempList = new ArrayList<>();
                for (SFCTaskIPT sfcTaskIPT : wNotReportList) {
                    if (wTempList.size() < wFPCRoutePartPoint.ReportFQTY) {
                        wTempList.add(sfcTaskIPT);
                        if (wTempList.size() >= wFPCRoutePartPoint.ReportFQTY) {
                            wToReportList.add(wTempList);
                            wTempList = new ArrayList<>();
                        }
                    }
                }
                //②依次报工
                int wAffectRows;
                List<Integer> wAPSTaskPartPointDayIDList = new ArrayList<>();
                for (List<SFCTaskIPT> sfcTaskIPTS : wToReportList) {
                    //③若是，则生成报工单
                    SFCTaskReport wSFCTaskReport = new SFCTaskReport();
                    wSFCTaskReport.OrderID = wSFCTaskIPT.OrderID;
                    wSFCTaskReport.OrderNo = wSFCTaskIPT.OrderNo;
                    wSFCTaskReport.RoutePartID = wSFCTaskIPT.RoutePartID;
                    wSFCTaskReport.RoutePartPointID = wSFCTaskIPT.RoutePartPointID;
                    wSFCTaskReport.ProductID = wSFCTaskIPT.ProductID;
                    wSFCTaskReport.TaskPartID = wSFCTaskIPT.TaskPartID;
                    wSFCTaskReport.TaskPartPointID = wSFCTaskIPT.TaskPartPointID;
                    wSFCTaskReport.TaskPartPointDayID = wSFCTaskIPT.TaskPartPointDayID;
                    wSFCTaskReport.TaskSelfID = wSFCTaskIPT.TaskSelfID;
                    wSFCTaskReport.WorkShopID = wSFCTaskIPT.WorkShopID;
                    wSFCTaskReport.WorkAreaID = wSFCTaskIPT.WorkAreaID;
                    wSFCTaskReport.StationID = wSFCTaskIPT.StationID;
                    wSFCTaskReport.StationDate = wSFCTaskIPT.StationDate;
                    wSFCTaskReport.ShiftType = wSFCTaskIPT.ShiftType;
                    wSFCTaskReport.ActNum = wFPCRoutePartPoint.ReportFQTY;
                    wSFCTaskReport.setUserInfo(wLoginUser);

                    if (wAPSTaskPartPointDayIDList.stream().noneMatch(p -> p == wSFCTaskReport.TaskPartPointDayID)) {
                        wAPSTaskPartPointDayIDList.add(wSFCTaskReport.TaskPartPointDayID);
                    }

                    if (wFPCRoutePartPoint.HandoverSemiProduct == 1) {
                        wSFCTaskReport.Status = SFCTaskReportStatus.TO_CONFIRM.getValue();
                        //维护待确认人
                        wSFCTaskReport.HandoverList = getHandoverList(wLoginUser, wFPCRoutePartPoint,
                                wSFCTaskReport.OrderID,
                                wErrorCode);
                        if (wSFCTaskReport.HandoverList == null || wSFCTaskReport.HandoverList.size() == 0) {
                            wResult.FaultCode += "未找到后工序交接人，请联系后工序班组长派工!";
                            return wResult;
                        }
                    } else {
                        wSFCTaskReport.ConfirmID = BMSEmployee.SysAdmin.ID;
                        wSFCTaskReport.Status = SFCTaskReportStatus.CONFIRMED.getValue();

                        //库存扣减
                        TaskTimerUtils.AddTask("StockDeduct",
                                () -> StockDeduct(wLoginUser, wSFCTaskReport, wErrorCode)
                        );

                        //更新计划实时进度
                        TaskTimerUtils.AddTask("UpdatesPlanRealTimeProgress",
                                () -> UpdatesPlanRealTimeProgress(wLoginUser,
                                        new ArrayList<>(Collections.singletonList(wSFCTaskIPT.TaskSelfID)),
                                        wAPSTaskPartPointDayIDList, wErrorCode)
                        );
                    }

                    wSFCTaskReport.ReportMode = SFCTaskReportMode.AUTO_REPORT.getValue();
                    mSFCTaskReportService.UpdatePo(wLoginUser, wSFCTaskReport, wErrorCode);

                    //④维护自检单的报工状态和报工单ID
                    for (SFCTaskIPT wItemIPT : sfcTaskIPTS) {
                        wItemIPT.setUserInfo(wLoginUser);
                        wItemIPT.IsReported = 1;
                        wItemIPT.TaskReportID = wSFCTaskReport.ID;
                    }
                    mSFCTaskIPTService.UpdatePoList(wLoginUser, sfcTaskIPTS, wErrorCode);

                    //维护工件的履历（报工信息）
                    TaskTimerUtils.AddTask("UpdateReportInfo",
                            () -> UpdateReportInfo(wLoginUser, wResult, wErrorCode,
                                    wParams, sfcTaskIPTS)
                    );

                    //返修计划状态维护、返修单状态更新、返修记录更新
                    TriggerRepairItemStatusReport(wLoginUser, sfcTaskIPTS);
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());

            long endTime = System.currentTimeMillis();
            log.info("SFC_AutoReport： " + (endTime - startTime) + "ms");
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 更新报工信息
     */
    private void UpdateReportInfo(BMSEmployee wLoginUser, ServiceResult<Integer> wResult,
                                  OutResult<Integer> wErrorCode, Map<String, Object> wParams,
                                  List<SFCTaskIPT> sfcTaskIPTS) {
        try {
            int wAffectRows;
            SFCWorkpieceRecord wSFCWorkpieceRecord;
            SFCWorkpieceResume wSFCWorkpieceResume;
            for (SFCTaskIPT wSFCTaskIPTItem : sfcTaskIPTS) {
                wParams.clear();
                wParams.put("CodeEQ", wSFCTaskIPTItem.ComponentCode);
                wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wSFCWorkpieceRecord != null && wSFCWorkpieceRecord.ID > 0) {
                    wParams.clear();
                    wParams.put("WorkpieceRecordID", wSFCWorkpieceRecord.ID);
                    wParams.put("StationID", wSFCTaskIPTItem.StationID);
                    wParams.put("RoutePartPointID", wSFCTaskIPTItem.RoutePartPointID);
                    wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (wSFCWorkpieceResume != null && wSFCWorkpieceResume.ID > 0) {
                        wSFCWorkpieceResume.setUserInfo(wLoginUser);
                        wSFCWorkpieceResume.TaskReportID = wSFCTaskIPTItem.TaskReportID;
                        wSFCWorkpieceResume.ReporterID = wLoginUser.ID;
                        wSFCWorkpieceResume.ReporterTime = Calendar.getInstance();
                        mSFCWorkpieceResumeService.UpdatePo(wLoginUser, wSFCWorkpieceResume,
                                wErrorCode);
                    }
                }
            }
        } catch (Exception ex) {
            log.error("UpdateReportInfo", ex);
        }
    }

    @Override
    public ServiceResult<List<SFCTaskIPT>> SFC_QueryReportTaskIPIList(BMSEmployee wLoginUser,
                                                                      Map<String, Object> wParams) {
        ServiceResult<List<SFCTaskIPT>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            wParams.put("WorkerIDList", wLoginUser.ID);
            wParams.put("Status", SFCTaskStatus.Done.getValue());
            wParams.put("IsReported", 0);
            wParams.put("TaskReportID", 0);
            wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());

            wResult.Result = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_HandReport(BMSEmployee wLoginUser, List<SFCTaskIPT> wSFCTaskIPTList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            long startTime = System.currentTimeMillis();


            OutResult<Integer> wErrorCode = new OutResult<>(0);
            Map<String, Object> wParams = new HashMap<String, Object>();

            //判断该工序是否已停序
            if (EXCServiceImpl.getInstance().EXC_IsRoutePartPointStop(wLoginUser, wSFCTaskIPTList.get(0).OrderID,
                    wSFCTaskIPTList.get(0).RoutePartPointID).Result) {
                wResult.FaultCode += StringUtils.Format("操作失败：工序【{0}】已停序!!!",
                        wSFCTaskIPTList.get(0).RoutePartPointName);
                return wResult;
            }

            //验证工件状态，若为`报废`，则不允许流转；
            String wComponentCodes = StringUtils.Join("','",
                    wSFCTaskIPTList.stream().map(p -> p.ComponentCode).distinct().collect(Collectors.toList()));
            List<Integer> wRecordIDList = SFCWorkpieceRecordDAO.getInstance().SFC_GetRecordIDList(wLoginUser,
                    wComponentCodes, wErrorCode);
            List<SFCWorkpieceRecord> sfcWorkpieceRecords = mSFCWorkpieceRecordService.SelectPOList(wLoginUser,
                    wRecordIDList, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            for (SFCTaskIPT wSFCTaskIPT : wSFCTaskIPTList) {
                Optional<SFCWorkpieceRecord> first =
                        sfcWorkpieceRecords.stream().filter(p -> p.Code.equals(wSFCTaskIPT.ComponentCode)).findFirst();
                if (first.isPresent()) {
                    SFCWorkpieceRecord sfcWorkpieceRecord = first.get();
                    if (sfcWorkpieceRecord.Status == SFCTaskStatus.Scarp.getValue()) {
                        wResult.FaultCode += StringUtils.Format("报工失败：工件【{0}】已报废!!!", wSFCTaskIPT.ComponentCode);
                        return wResult;
                    }
                }
            }

            List<Integer> wTaskSelfIDList =
                    wSFCTaskIPTList.stream().map(p -> p.TaskSelfID).distinct().collect(Collectors.toList());
            List<SFCTaskIPT> wTaskList;
            int wAffectRows;
            SFCTaskIPT wSFCTaskIPT;

            FPCRoutePartPoint wFPCRoutePartPoint;
            List<Integer> wAPSTaskPartPointDayIDList = new ArrayList<>();
            for (int wTaskSelfID : wTaskSelfIDList) {
                wTaskList =
                        wSFCTaskIPTList.stream().filter(p -> p.TaskSelfID == wTaskSelfID).collect(Collectors.toList());
                wSFCTaskIPT = wTaskList.get(0);
                //查询工艺工序
                wParams.clear();
                wParams.put("ID", wSFCTaskIPT.RoutePartPointID);
                wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }

                //③若是，则生成报工单
                SFCTaskReport wSFCTaskReport = new SFCTaskReport();
                wSFCTaskReport.OrderID = wSFCTaskIPT.OrderID;
                wSFCTaskReport.OrderNo = wSFCTaskIPT.OrderNo;
                wSFCTaskReport.RoutePartID = wSFCTaskIPT.RoutePartID;
                wSFCTaskReport.RoutePartPointID = wSFCTaskIPT.RoutePartPointID;
                wSFCTaskReport.ProductID = wSFCTaskIPT.ProductID;
                wSFCTaskReport.TaskPartID = wSFCTaskIPT.TaskPartID;
                wSFCTaskReport.TaskPartPointID = wSFCTaskIPT.TaskPartPointID;
                wSFCTaskReport.TaskPartPointDayID = wSFCTaskIPT.TaskPartPointDayID;
                wSFCTaskReport.TaskSelfID = wSFCTaskIPT.TaskSelfID;
                wSFCTaskReport.WorkShopID = wSFCTaskIPT.WorkShopID;
                wSFCTaskReport.WorkAreaID = wSFCTaskIPT.WorkAreaID;
                wSFCTaskReport.StationID = wSFCTaskIPT.StationID;
                wSFCTaskReport.StationDate = wSFCTaskIPT.StationDate;
                wSFCTaskReport.ShiftType = wSFCTaskIPT.ShiftType;
                wSFCTaskReport.ActNum = wTaskList.size();
                wSFCTaskReport.RoutePartPointName = wSFCTaskIPT.RoutePartPointName;
                wSFCTaskReport.IsRepairPlan = wSFCTaskIPT.IsRepairPlan;
                wSFCTaskReport.ProductRepairID = wSFCTaskIPT.ProductRepairID;
                wSFCTaskReport.setUserInfo(wLoginUser);

                if (wAPSTaskPartPointDayIDList.stream().noneMatch(p -> p == wSFCTaskReport.TaskPartPointDayID)) {
                    wAPSTaskPartPointDayIDList.add(wSFCTaskReport.TaskPartPointDayID);
                }

                //派工单
                wParams.clear();
                wParams.put("ID", wSFCTaskIPT.TaskSelfID);
                SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams, wErrorCode);
                if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
                //返修报工不需要流转到后工序做确认
                if (wSFCTaskSelf.IsRepairPlan == 1) {
                    wSFCTaskReport.Status = SFCTaskReportStatus.CONFIRMED.getValue();
                } else {
                    if (wFPCRoutePartPoint.HandoverSemiProduct == 1) {
                        wSFCTaskReport.Status = SFCTaskReportStatus.TO_CONFIRM.getValue();
                        //维护待确认人
                        wSFCTaskReport.HandoverList = getHandoverList(wLoginUser, wFPCRoutePartPoint,
                                wSFCTaskReport.OrderID,
                                wErrorCode);
                        if (wSFCTaskReport.HandoverList == null || wSFCTaskReport.HandoverList.size() == 0) {
                            wResult.FaultCode += "未找到后工序交接人，请联系后工序班组长派工!";
                            return wResult;
                        }
                    } else {
                        wSFCTaskReport.ConfirmID = BMSEmployee.SysAdmin.ID;
                        wSFCTaskReport.Status = SFCTaskReportStatus.CONFIRMED.getValue();

                        //库存扣减
                        TaskTimerUtils.AddTask("StockDeduct",
                                () -> StockDeduct(wLoginUser, wSFCTaskReport, wErrorCode)
                        );

                    }
                }

                wSFCTaskReport.ReportMode = SFCTaskReportMode.HAND_REPORT.getValue();
                wAffectRows = mSFCTaskReportService.UpdatePo(wLoginUser, wSFCTaskReport, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
                //④维护自检单的报工状态和报工单ID
                for (SFCTaskIPT wItemIPT : wTaskList) {
                    wItemIPT.setUserInfo(wLoginUser);
                    if (wFPCRoutePartPoint.HandoverSemiProduct != 1) {
                        wItemIPT.IsReported = 1;
                    }
                    wItemIPT.TaskReportID = wSFCTaskReport.ID;
                    mSFCTaskIPTService.UpdatePo(wLoginUser, wItemIPT, wErrorCode);

                    //维护箱体工件的状态
                    if (wFPCRoutePartPoint.NextPartPointID == null
                            || wFPCRoutePartPoint.NextPartPointID.size() <= 0
                            && wSFCTaskSelf.ComponentType == PDMComponentTypes.Box.getValue() && StringUtils.isNotEmpty(wItemIPT.ComponentCode)) {
                        wParams.clear();
                        wParams.put("CodeEQ", wItemIPT.ComponentCode);
                        SFCWorkpieceRecord sfcWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                                wErrorCode);
                        if (sfcWorkpieceRecord != null && sfcWorkpieceRecord.ID > 0) {
                            sfcWorkpieceRecord.Status = SFCTaskStatus.Done.getValue();
                            mSFCWorkpieceRecordService.UpdatePo(wLoginUser, sfcWorkpieceRecord, wErrorCode);
                        }
                    }
                }
            }

            //维护工件的履历（报工信息）
            TaskTimerUtils.AddTask("TriggerProductRepairItemStatusUpdateLogic",
                    () -> UpdateReportInfo(wLoginUser, wResult, wErrorCode, wParams, wSFCTaskIPTList)
            );

            //返修计划状态维护、返修单状态更新、返修记录更新
            TriggerRepairItemStatusReport(wLoginUser, wSFCTaskIPTList);

            //更新计划实时进度
            TaskTimerUtils.AddTask("UpdatesPlanRealTimeProgress",
                    () -> UpdatesPlanRealTimeProgress(wLoginUser, wTaskSelfIDList, wAPSTaskPartPointDayIDList,
                            wErrorCode)
            );

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());

            long endTime = System.currentTimeMillis();
            log.info("SFC_HandReport： " + (endTime - startTime) + "ms");
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 更新计划实时进度
     */
    @Override
    public void UpdatesPlanRealTimeProgress(BMSEmployee wLoginUser, List<Integer> wTaskSelftIDList,
                                            List<Integer> wTaskPartPointDayIDList,
                                            OutResult<Integer> wErrorCode) {
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();

            //查询产品组成
            List<FPCProductCom> wFPCProductComList = mFPCProductComService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            Map<Integer, List<FPCProductCom>> wProductIDComMap = wFPCProductComList.stream()
                    .collect(Collectors.groupingBy(FPCProductCom::getProductID));

            List<Integer> wTaskPartPointIDList = new ArrayList<>();
            List<SFCTaskSelf> wSFCTaskSelfList = new ArrayList<>();
            List<Integer> wOrderIDList = new ArrayList<>();
            boolean wIsBoxReport = false;
            for (int wTaskSelftID : wTaskSelftIDList) {
                wParams.clear();
                wParams.put("ID", wTaskSelftID);
                SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams, wErrorCode);
                if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0 || wSFCTaskSelf.IsRepairPlan == 1) {
                    return;
                }

                wSFCTaskSelfList.add(wSFCTaskSelf);

                if (wTaskPartPointIDList.stream().noneMatch(p -> p == wSFCTaskSelf.TaskPartPointID)) {
                    wTaskPartPointIDList.add(wSFCTaskSelf.TaskPartPointID);
                }

                //添加订单ID
                if (wOrderIDList.stream().noneMatch(p -> p == wSFCTaskSelf.OrderID)) {
                    wOrderIDList.add(wSFCTaskSelf.OrderID);
                }

                //维护派工任务的实际执行进度
                SFCTaskSelfDAO.getInstance().SFC_UpdatesPlanRealTimeProgress(wLoginUser, wSFCTaskSelf,
                        wProductIDComMap, wErrorCode);

                if (wSFCTaskSelf.ComponentType == PDMComponentTypes.Box.getValue()) {
                    wIsBoxReport = true;
                }
            }

            //维护日计划的实际执行进度
            SFC_UpdatesPlanRealTimeProgress_DayPlan(wLoginUser, wTaskPartPointDayIDList, wSFCTaskSelfList,
                    wProductIDComMap, wErrorCode);
            //维护工序计划的实际执行进度与历史执行进度
            SFC_UpdatesPlanRealTimeProgress_PartPointPlan(wLoginUser, wTaskPartPointIDList, wSFCTaskSelfList,
                    wProductIDComMap, wErrorCode);
            //维护周计划、月计划的实际执行进度
            SFC_UpdatesPlanRealTimeProgress_WeekMonthPlan(wLoginUser, wTaskPartPointIDList, wSFCTaskSelfList,
                    wErrorCode);
            //维护订单的实际完成数量
            if (wOrderIDList.size() > 0 && wIsBoxReport) {
                SFC_UpdateOrderRealFQTY(wLoginUser, wOrderIDList, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("UpdatesPlanRealTimeProgress", ex);
        }
    }

    /**
     * 维护订单的实际完成数量
     */
    private void SFC_UpdateOrderRealFQTY(BMSEmployee wLoginUser, List<Integer> wOrderIDList,
                                         OutResult<Integer> wErrorCode) {
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            for (Integer wOrderID : wOrderIDList) {
                //①查询成品工件记录
                wParams.clear();
                wParams.put("OrderID", wOrderID);
                wParams.put("ComponentType", PDMComponentTypes.Product.getValue());
                List<SFCWorkpieceRecord> sfcWorkpieceRecords = mSFCWorkpieceRecordService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(),
                        wErrorCode);
                if (sfcWorkpieceRecords == null || sfcWorkpieceRecords.size() <= 0) {
                    return;
                }
                //②更新订单数量
                float wRealFQTY = sfcWorkpieceRecords.size();
                //③维护订单状态
                wParams.clear();
                wParams.put("ID", wOrderID);
                OMSOrder omsOrder = mOMSOrderService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (omsOrder == null || omsOrder.ID <= 0) {
                    continue;
                }
                omsOrder.FQTYActual = (int) wRealFQTY;
                if (omsOrder.FQTYActual >= omsOrder.FQTYPlan) {
                    omsOrder.Status = OMSOrderStatus.FinishedWork.getValue();
                    omsOrder.RealFinishDate = Calendar.getInstance();
                }
                mOMSOrderService.UpdatePo(wLoginUser, omsOrder, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("SFC_UpdateOrderRealFQTY", ex);
        }
    }

    /**
     * 维护周计划、月计划的实际执行进度
     */
    private void SFC_UpdatesPlanRealTimeProgress_WeekMonthPlan(BMSEmployee wLoginUser,
                                                               List<Integer> wTaskPartPointIDList,
                                                               List<SFCTaskSelf> wSFCTaskSelfList,
                                                               OutResult<Integer> wErrorCode) {
        try {
            if (wTaskPartPointIDList == null || wTaskPartPointIDList.size() == 0) {
                return;
            }

            //获取工艺数据
            List<Integer> wRouteIDList =
                    wSFCTaskSelfList.stream().map(p -> p.RouteID).distinct().collect(Collectors.toList());
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("RouteID", StringUtils.Join(",", wRouteIDList));
            wParams.put("Active", 1);
            List<FPCRoutePartPoint> wFPCRoutePartPointList = mFPCRoutePartPointService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wFPCRoutePartPointList == null || wFPCRoutePartPointList.size() == 0) {
                return;
            }

            Map<Integer, List<FPCRoutePartPoint>> wRouteIDTaskPartPointMap = wFPCRoutePartPointList.stream()
                    .collect(Collectors.groupingBy(FPCRoutePartPoint::getRouteID));

            SFCTaskSelf wSFCTaskSelf;
            for (int wTaskPartPointID : wTaskPartPointIDList) {
                wSFCTaskSelf =
                        wSFCTaskSelfList.stream().filter(p -> p.TaskPartPointID == wTaskPartPointID).findFirst().get();
                //判断当前工序是否为尾工序
                if (!wRouteIDTaskPartPointMap.containsKey(wSFCTaskSelf.RouteID)) {
                    continue;
                }
                List<FPCRoutePartPoint> fpcRoutePartPoints = wRouteIDTaskPartPointMap.get(wSFCTaskSelf.RouteID);
                boolean wIsLast = FPC_IsLast(wLoginUser, wSFCTaskSelf.RoutePartPointID, fpcRoutePartPoints,
                        wErrorCode);
                if (wIsLast) {
                    //更新周计划的进度
                    SFCTaskSelfDAO.getInstance().SFC_UpdatesPlanRealTimeProgress_WeekPlan(wLoginUser, wSFCTaskSelf
                            , wErrorCode);

                    //更新计划的状态
                    wParams.clear();
                    wParams.put("ID", wSFCTaskSelf.TaskPartID);
                    wParams.put("Active", 1);
                    APSTaskPart wAPSTaskPart = mAPSTaskPartService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (wAPSTaskPart != null && wAPSTaskPart.ID > 0 && wAPSTaskPart.ActNum >= wAPSTaskPart.PlantNum) {
                        wAPSTaskPart.Status = APSTaskStatus.Done.getValue();
                        mAPSTaskPartService.UpdatePo(wLoginUser, wAPSTaskPart, wErrorCode);
                    }

                    //更新月计划的进度
                    SFCTaskSelfDAO.getInstance().SFC_UpdatesPlanRealTimeProgress_MonthPlan(wLoginUser, wSFCTaskSelf
                            , wErrorCode);
                }
            }
        } catch (Exception ex) {
            log.error("SFC_UpdatesPlanRealTimeProgress_WeekMonthPlan", ex);
        }
    }

    /**
     * 判断当前工序是否为尾工序
     */
    private boolean FPC_IsLast(BMSEmployee wLoginUser, int wRoutePartPointID,
                               List<FPCRoutePartPoint> fpcRoutePartPoints, OutResult<Integer> wErrorCode) {
        boolean wResult = false;
        try {
            //查找工序
            if (fpcRoutePartPoints.stream().noneMatch(p -> p.ID == wRoutePartPointID)) {
                return wResult;
            }

            FPCRoutePartPoint wFPCRoutePartPoint =
                    fpcRoutePartPoints.stream().filter(p -> p.ID == wRoutePartPointID).findFirst().get();
            //查找后工序
            List<FPCRoutePartPoint> wNextList =
                    fpcRoutePartPoints.stream().filter(p -> p.PrevPartPointID == wFPCRoutePartPoint.ID
                            || wFPCRoutePartPoint.NextPartPointID.stream().anyMatch(q -> q == p.ID)).collect(Collectors.toList());
            if (wNextList == null || wNextList.size() == 0 || wNextList.stream().anyMatch(p -> p.RoutePartID != wFPCRoutePartPoint.RoutePartID)) {
                wResult = true;
            }
        } catch (Exception ex) {
            log.error("FPC_IsLast", ex);
        }
        return wResult;
    }

    /**
     * 维护工序计划的实际执行进度与历史执行进度
     */
    private void SFC_UpdatesPlanRealTimeProgress_PartPointPlan(BMSEmployee wLoginUser,
                                                               List<Integer> wTaskPartPointIDList,
                                                               List<SFCTaskSelf> wSFCTaskSelfList,
                                                               Map<Integer, List<FPCProductCom>> wProductIDComMap,
                                                               OutResult<Integer> wErrorCode) {
        try {
            if (wTaskPartPointIDList == null || wTaskPartPointIDList.size() == 0) {
                return;
            }

            //今日日期
            Calendar wSTime = CalendarTool.getDate();

            SFCTaskSelf wSFCTaskSelf;
            Map<String, Object> wParams = new HashMap<String, Object>();
            for (int wTaskPartPointID : wTaskPartPointIDList) {
                wParams.clear();
                wParams.put("ID", wTaskPartPointID);
                wParams.put("Active", 1);
                APSTaskPartPoint wAPSTaskPartPoint = mAPSTaskPartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);

                SFCTaskSelfDAO.getInstance().SFC_UpdatesPlanRealTimeProgress_PartPointPlan(wLoginUser,
                        wAPSTaskPartPoint,
                        wProductIDComMap, wErrorCode);
                SFCTaskSelfDAO.getInstance().SFC_UpdatesPlanRealTimeProgress_PartPointPlan_His(wLoginUser,
                        wAPSTaskPartPoint,
                        wProductIDComMap, wSTime, wErrorCode);
                //更新计划的状态
                wParams.clear();
                wParams.put("ID", wTaskPartPointID);
                wParams.put("Active", 1);
                wAPSTaskPartPoint = mAPSTaskPartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);

                if (wAPSTaskPartPoint != null && wAPSTaskPartPoint.ID > 0 && wAPSTaskPartPoint.ActNum >= wAPSTaskPartPoint.PlanNum) {
                    wAPSTaskPartPoint.Status = APSTaskStatus.Done.getValue();
                    mAPSTaskPartPointService.UpdatePo(wLoginUser, wAPSTaskPartPoint, wErrorCode);
                }
            }
        } catch (Exception ex) {
            log.error("SFC_UpdatesPlanRealTimeProgress_PartPointPlan", ex);
        }
    }

    /**
     * 维护日计划的实际执行进度
     */
    private void SFC_UpdatesPlanRealTimeProgress_DayPlan(BMSEmployee wLoginUser,
                                                         List<Integer> wTaskPartPointDayIDList,
                                                         List<SFCTaskSelf> wSFCTaskSelfList,
                                                         Map<Integer, List<FPCProductCom>> wProductIDComMap,
                                                         OutResult<Integer> wErrorCode) {
        try {
            if (wTaskPartPointDayIDList == null || wTaskPartPointDayIDList.size() == 0) {
                return;
            }

            Map<String, Object> wParams = new HashMap<String, Object>();
            for (int wTaskPartPointDayID : wTaskPartPointDayIDList) {
                wParams.clear();
                wParams.put("ID", wTaskPartPointDayID);
                APSTaskPartPointDay apsTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams,
                        wErrorCode);

                SFCTaskSelfDAO.getInstance().SFC_UpdatesPlanRealTimeProgress_DayPlan(wLoginUser, apsTaskPartPointDay,
                        wTaskPartPointDayID,
                        wProductIDComMap, wErrorCode);
                //再次查询，获取进度
                wParams.clear();
                wParams.put("ID", wTaskPartPointDayID);
                apsTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams,
                        wErrorCode);

                //更新计划的状态
                if (apsTaskPartPointDay != null && apsTaskPartPointDay.ID > 0 && apsTaskPartPointDay.ActNum >= apsTaskPartPointDay.PlantNum) {
                    apsTaskPartPointDay.Status = APSTaskStatus.Done.getValue();
                    mAPSTaskPartPointDayService.UpdatePo(wLoginUser, apsTaskPartPointDay, wErrorCode);
                }
            }
        } catch (Exception ex) {
            log.error("SFC_UpdatesPlanRealTimeProgress_DayPlan", ex);
        }
    }

    /**
     * 返修任务报工逻辑
     */
    private void TriggerRepairItemStatusReport(BMSEmployee wLoginUser, List<SFCTaskIPT> wSFCTaskIPTList) {
        try {
            for (SFCTaskIPT wSFCTaskIPT : wSFCTaskIPTList) {
                TaskTimerUtils.AddTask("TriggerProductRepairItemStatusUpdateLogic",
                        () -> EXCServiceImpl.getInstance().EXC_ProductRepairItemStatusUpdateLogic(wLoginUser,
                                wSFCTaskIPT, true)
                );
            }
        } catch (Exception ex) {
            log.error("TriggerRepairItemStatusReport", ex);
        }
    }

    /**
     * 获取后工序交接人
     */
    private List<Integer> getHandoverList(BMSEmployee wLoginUser, FPCRoutePartPoint wFPCRoutePartPoint,
                                          int wOrderID, OutResult<Integer> wErrorCode) {
        List<Integer> wResult = new ArrayList<Integer>();
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("PrevPartPointID", wFPCRoutePartPoint.ID);
            FPCRoutePartPoint wNextRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wNextRoutePartPoint == null || wNextRoutePartPoint.ID <= 0) {
                return wResult;
            }

            wParams.clear();
            wParams.put("OrderID", wOrderID);
            wParams.put("RoutePartID", wNextRoutePartPoint.RoutePartID);
            wParams.put("RoutePartPointID", wNextRoutePartPoint.ID);
            List<SFCTaskSelf> wSFCTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            if (wSFCTaskSelfList == null || wSFCTaskSelfList.size() <= 0) {
                return wResult;
            }

            wResult = wSFCTaskSelfList.stream().map(p -> p.WorkerID).distinct().collect(Collectors.toList());
            for (SFCTaskSelf wSFCTaskSelf : wSFCTaskSelfList) {
                if (wResult.stream().noneMatch(p -> p == wSFCTaskSelf.EditorID)) {
                    wResult.add(wSFCTaskSelf.EditorID);
                }
            }

            //若无数据，则找下道工序工位班组长
            if (wResult.size() == 0) {
                wParams.clear();
                wParams.put("StationID", StringUtils.Join(",", wNextRoutePartPoint.StationID));
                wParams.put("StationActive", 1);
                List<BMSClassRange> wBMSClassRangeList = mBMSClassRangeService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(), wErrorCode);
                if (wBMSClassRangeList.size() > 0) {
                    List<Integer> wClassInfoIDList =
                            wBMSClassRangeList.stream().map(p -> p.ClassInfoID).distinct().collect(Collectors.toList());
                    wParams.clear();
                    List<BMSClassInfo> wBMSClassInfoList = mBMSClassInfoService.SelectPOList(wLoginUser,
                            wClassInfoIDList
                            , wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
                    for (BMSClassInfo wBMSClassInfo : wBMSClassInfoList) {
                        if (StringUtils.isNotEmpty(wBMSClassInfo.MonitorIDs)) {
                            wResult.addAll(StringUtils.parseIntList(wBMSClassInfo.MonitorIDs.split(",")));
                        }
                    }
                }
                wResult = wResult.stream().distinct().collect(Collectors.toList());
            }

            mSFCTaskSelfService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(), wErrorCode);
        } catch (Exception ex) {
            log.error("getHandoverList", ex);
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<FPCRouteStep>> SFC_QueryCheckItemsByOrder(BMSEmployee wLoginUser, int wOrderID,
                                                                        int wRoutePartID, int wRoutePartPointID) {
        ServiceResult<List<FPCRouteStep>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①根据订单、工位、工序查询自检记录，若没有，则报错提示
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("OrderID", wOrderID);
            wParams.put("RoutePartID", wRoutePartID);
            wParams.put("RoutePartPointID", wRoutePartPointID);
            wParams.put("Status", SFCTaskStatus.Done.getValue());
            SFCTaskIPT wSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wSFCTaskIPT == null || wSFCTaskIPT.ID <= 0) {
                wResult.FaultCode += "未查询到该工序已完工的自检记录，请先完成自检操作!";
                return wResult;
            }

            wParams.clear();
            wParams.put("RoutePartPointID", wRoutePartPointID);
            wParams.put("Active", 1);
            wParams.put("IsSpecialCheck", 1);
            wResult.Result = mFPCRouteStepService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            wResult.Result.sort(Comparator.comparing(FPCRouteStep::getStepOrder));

            //根据派工任务、检验类型查询检验单
            wParams.clear();
            wParams.put("OrderID", wOrderID);
            wParams.put("RoutePartPointID", wRoutePartPointID);
            wParams.put("Type", FPCCheckType.SPECIAL_CHECK.getValue());
            wParams.put("Status", SFCTaskStatus.Default.getValue());
            List<SFCTaskIPT> wSFCTaskIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            //根据检验单查询检验值
            List<IPTValue> wValueList = new ArrayList<>();
            if (wSFCTaskIPTList.size() > 0) {
                wParams.clear();
                wParams.put("TaskID", StringUtils.Join(",",
                        wSFCTaskIPTList.stream().map(p -> p.ID).distinct().collect(Collectors.toList())));
                wValueList = mIPTValueService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(), wErrorCode);
            }
            //返回前端
            wResult.CustomResult.put("SFCTaskIPTList", wSFCTaskIPTList);
            wResult.CustomResult.put("ValueList", wValueList);


            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<SFCTaskIPT> SFC_CreateSpecialCheckTaskByOrder(BMSEmployee wLoginUser, int wOrderID,
                                                                       int wRoutePartID, int wRoutePartPointID,
                                                                       String wWorkpieceNo) {
        ServiceResult<SFCTaskIPT> wResult = new ServiceResult<>();
        wResult.Result = new SFCTaskIPT();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //查询自检单
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("OrderID", wOrderID);
            wParams.put("RoutePartID", wRoutePartID);
            wParams.put("RoutePartPointID", wRoutePartPointID);
            wParams.put("Status", SFCTaskStatus.Done.getValue());
            wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
            wParams.put("ComponentCode", wWorkpieceNo);
            SFCTaskIPT wSelfTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wSelfTaskIPT == null || wSelfTaskIPT.ID <= 0) {
                wResult.FaultCode += StringUtils.Format("工件【{0}】未做该工序的自检!", wWorkpieceNo);
                return wResult;
            }

            //创建专检单
            int wAffectRows;
            wParams.clear();
            wParams.put("OrderID", wOrderID);
            wParams.put("RoutePartID", wRoutePartID);
            wParams.put("RoutePartPointID", wRoutePartPointID);
            wParams.put("Type", FPCCheckType.SPECIAL_CHECK.getValue());
            wParams.put("ComponentCode", wWorkpieceNo);
            SFCTaskIPT wSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskIPT.ID > 0) {
                wResult.FaultCode += StringUtils.Format("提示：【{0}】该工件已创建【{1}】单!", wWorkpieceNo,
                        FPCCheckType.SPECIAL_CHECK.getLabel());
                wResult.Result = wSFCTaskIPT;
                return wResult;
            } else {
                //根据日计划ID查询派工记录
                wParams.clear();
                wParams.put("TaskPartPointDayID", wSelfTaskIPT.TaskPartPointDayID);
                List<SFCTaskSelf> wSFCTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(), wErrorCode);

                wSFCTaskIPT = new SFCTaskIPT();
                wSFCTaskIPT.setUserInfo(wLoginUser);
                wSFCTaskIPT.OrderID = wSelfTaskIPT.OrderID;
                wSFCTaskIPT.OrderNo = wSelfTaskIPT.OrderNo;
                wSFCTaskIPT.RoutePartID = wSelfTaskIPT.RoutePartID;
                wSFCTaskIPT.RoutePartPointID = wSelfTaskIPT.RoutePartPointID;
                wSFCTaskIPT.ProductID = wSelfTaskIPT.ProductID;
                wSFCTaskIPT.TaskPartID = wSelfTaskIPT.TaskPartID;
                wSFCTaskIPT.TaskPartPointID = wSelfTaskIPT.TaskPartPointID;
                wSFCTaskIPT.TaskPartPointDayID = wSelfTaskIPT.TaskPartPointDayID;
                wSFCTaskIPT.TaskSelfID = wSelfTaskIPT.TaskSelfID;
                wSFCTaskIPT.WorkShopID = wSelfTaskIPT.WorkShopID;
                wSFCTaskIPT.WorkAreaID = wSelfTaskIPT.WorkAreaID;
                wSFCTaskIPT.StationID = wSelfTaskIPT.StationID;
                wSFCTaskIPT.StationDate = wSelfTaskIPT.StationDate;
                wSFCTaskIPT.Result = 1;
                wSFCTaskIPT.ShiftType = wSelfTaskIPT.ShiftType;
                wSFCTaskIPT.WorkerIDList = new ArrayList<>();
                if (wSFCTaskSelfList.size() > 0) {
                    wSFCTaskIPT.WorkerIDList =
                            wSFCTaskSelfList.stream().map(p -> p.WorkerID).distinct().collect(Collectors.toList());
                }
                wSFCTaskIPT.Status = SFCTaskStatus.Default.getValue();
                wSFCTaskIPT.Type = FPCCheckType.SPECIAL_CHECK.getValue();
                wSFCTaskIPT.ComponentCode = wWorkpieceNo;
                wAffectRows = mSFCTaskIPTService.UpdatePo(wLoginUser, wSFCTaskIPT, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    wResult.Result = wSFCTaskIPT;
                    return wResult;
                }
            }
            //⑤返回前端
            wResult.Result = wSFCTaskIPT;

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<String> SFC_CheckWorkpieceValue(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT,
                                                         IPTValue wIPTValue) {
        ServiceResult<String> wResult = new ServiceResult<>();
        wResult.Result = "";
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("Code", wIPTValue.Value);
            SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                    wErrorCode);

            if (wIPTValue.ComponentType == PDMComponentTypes.Component.getValue()) {//元件
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    wResult.Result = StringUtils.Format("元件码【{0}】未扫码录入系统!", wIPTValue.Value);
                    return wResult;
                }

                if (wSFCWorkpieceRecord.OrderID != wSFCTaskIPT.OrderID) {
                    wResult.Result = StringUtils.Format("元件码【{0}】已与订单【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.OrderNo);
                    return wResult;
                }

                if (StringUtils.isNotEmpty(wSFCWorkpieceRecord.PackageNo) && !wSFCWorkpieceRecord.PackageNo.equals(wSFCTaskIPT.ComponentCode)) {
                    wResult.Result = StringUtils.Format("元件码【{0}】已与成品码【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.PackageNo);
                    return wResult;
                }
            } else if (
                    wIPTValue.ComponentType == PDMComponentTypes.Core.getValue()) {//芯子
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    wResult.Result = StringUtils.Format("芯子码【{0}】未扫码录入系统!", wIPTValue.Value);
                    return wResult;
                }

                if (wSFCWorkpieceRecord.OrderID != wSFCTaskIPT.OrderID) {
                    wResult.Result = StringUtils.Format("芯子码【{0}】已与订单【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.OrderNo);
                    return wResult;
                }

                if (StringUtils.isNotEmpty(wSFCWorkpieceRecord.PackageNo) && !wSFCWorkpieceRecord.PackageNo.equals(wSFCTaskIPT.ComponentCode)) {
                    wResult.Result = StringUtils.Format("芯子码【{0}】已与成品码【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.PackageNo);
                    return wResult;
                }
            } else if (wIPTValue.ComponentType == PDMComponentTypes.Frock.getValue()) {//成品
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    wResult.Result = StringUtils.Format("成品码【{0}】未扫码录入系统!", wIPTValue.Value);
                    return wResult;
                }

                if (wSFCWorkpieceRecord.OrderID != wSFCTaskIPT.OrderID) {
                    wResult.Result = StringUtils.Format("成品码【{0}】已与订单【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.OrderNo);
                    return wResult;
                }

                if (StringUtils.isNotEmpty(wSFCWorkpieceRecord.CaseNo) && !wSFCWorkpieceRecord.CaseNo.equals(wSFCTaskIPT.ComponentCode)) {
                    wResult.Result = StringUtils.Format("成品码【{0}】已与外壳码【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.CaseNo);
                    return wResult;
                }
            } else if (wIPTValue.ComponentType == PDMComponentTypes.ProductCase.getValue()) {//外壳
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    wResult.Result = StringUtils.Format("外壳码【{0}】未扫码录入系统!", wIPTValue.Value);
                    return wResult;
                }

                if (wSFCWorkpieceRecord.OrderID != wSFCTaskIPT.OrderID) {
                    wResult.Result = StringUtils.Format("外壳码【{0}】已与订单【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.OrderNo);
                    return wResult;
                }

                if (StringUtils.isNotEmpty(wSFCWorkpieceRecord.BoxNo) && !wSFCWorkpieceRecord.BoxNo.equals(wSFCTaskIPT.ComponentCode)) {
                    wResult.Result = StringUtils.Format("外壳码【{0}】已与装箱码【{1}】绑定!", wIPTValue.Value,
                            wSFCWorkpieceRecord.BoxNo);
                    return wResult;
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_BindingNo(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT,
                                                List<IPTValue> wIPTValueList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            if (wIPTValueList == null || wIPTValueList.size() <= 0 || wSFCTaskIPT == null || wSFCTaskIPT.ID <= 0
                    || StringUtils.isEmpty(wSFCTaskIPT.ComponentCode)) {
                wResult.FaultCode += RetCode.SERVER_RST_ERROR_OUT;
                return wResult;
            }

            Map<String, Object> wParams = new HashMap<String, Object>();

            SFCWorkpieceRecord wSFCWorkpieceRecord;
            int wAffectRows;
            for (IPTValue wIPTValue : wIPTValueList) {
                if (wIPTValue.ComponentType <= 0) {
                    continue;
                }

                wParams.clear();
                wParams.put("Code", wIPTValue.Value);
                wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    continue;
                }

                switch (PDMComponentTypes.getEnumType(wIPTValue.ComponentType)) {
                    case Component://元件
                    case Core://芯子
                        wSFCWorkpieceRecord.PackageNo = wSFCTaskIPT.ComponentCode;
                        wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                        wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord,
                                wErrorCode);
                        if (wAffectRows <= 0) {
                            wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                            return wResult;
                        }
                        break;
                    case Frock://成品
                        wSFCWorkpieceRecord.CaseNo = wSFCTaskIPT.ComponentCode;
                        wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                        wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord,
                                wErrorCode);
                        if (wAffectRows <= 0) {
                            wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                            return wResult;
                        }
                        break;
                    case ProductCase://外壳
                        wSFCWorkpieceRecord.BoxNo = wSFCTaskIPT.ComponentCode;
                        wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                        wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord,
                                wErrorCode);
                        if (wAffectRows <= 0) {
                            wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                            return wResult;
                        }
                        break;
                    default:
                        break;
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<SFCTaskReport>> SFC_QueryToConfirmList(BMSEmployee wLoginUser,
                                                                     Map<String, Object> wParams,
                                                                     Pagination wPagination) {
        ServiceResult<List<SFCTaskReport>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            wParams.put("HandoverList", wLoginUser.ID);
            wParams.put("Status", SFCTaskReportStatus.TO_CONFIRM.getValue());
            wResult.Result = mSFCTaskReportService.SelectPOList(wLoginUser, wParams, wPagination, wErrorCode);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_TaskReportConfirm(BMSEmployee wLoginUser, SFCTaskReport wSFCTaskReport) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①查询关联的自检单
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("TaskReportID", wSFCTaskReport.ID);
            wParams.put("IsReported", 0);
            List<SFCTaskIPT> wSFCTaskIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            //②自检单报工状态维护
            int wAffectRows;
            for (SFCTaskIPT wSFCTaskIPT : wSFCTaskIPTList) {
                wSFCTaskIPT.IsReported = 1;
                wAffectRows = mSFCTaskIPTService.UpdatePo(wLoginUser, wSFCTaskIPT, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
            }
            //③报工单状态修改
            wSFCTaskReport.Status = SFCTaskReportStatus.CONFIRMED.getValue();
            wSFCTaskReport.ConfirmID = wLoginUser.ID;
            wSFCTaskReport.ConfirmTime = Calendar.getInstance();
            wAffectRows = mSFCTaskReportService.UpdatePo(wLoginUser, wSFCTaskReport, wErrorCode);
            if (wAffectRows <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //库存扣减
            TaskTimerUtils.AddTask("StockDeduct",
                    () -> StockDeduct(wLoginUser, wSFCTaskReport, wErrorCode)
            );

            //更新计划实时进度
            TaskTimerUtils.AddTask("UpdatesPlanRealTimeProgress",
                    () -> UpdatesPlanRealTimeProgress(wLoginUser,
                            new ArrayList<>(Collections.singletonList(wSFCTaskReport.TaskSelfID)),
                            new ArrayList<>(Collections.singletonList(wSFCTaskReport.TaskPartPointDayID)), wErrorCode)
            );

            //返修计划状态维护、返修单状态更新、返修记录更新
            TriggerRepairItemStatusReport(wLoginUser, wSFCTaskIPTList);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 报工后，库存扣减
     */
    private void StockDeduct(BMSEmployee wLoginUser, SFCTaskReport wSFCTaskReport, OutResult<Integer> wErrorCode) {
        try {
            //①自动原材料扣减
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCTaskReport.TaskPartPointDayID);
            APSTaskPartPointDay wAPSTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wAPSTaskPartPointDay == null || wAPSTaskPartPointDay.ID <= 0) {
                log.error(ExceptionTypes.Logic.getLabel());
                return;
            }

            wParams.clear();
            wParams.put("ID", wSFCTaskReport.TaskSelfID);
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0) {
                log.error(ExceptionTypes.Logic.getLabel());
                return;
            }

            //获取工位实时物料
            wParams.clear();
            wParams.put("StationID", wSFCTaskSelf.StationID);
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            List<SFCStationMaterial> wSFCStationMaterialList = mSFCStationMaterialService.SelectPOList(wLoginUser,
                    wParams, Pagination.getNewMaxSize(), wErrorCode);
            if (wSFCStationMaterialList == null || wSFCStationMaterialList.size() <= 0) {
                log.error(ExceptionTypes.Logic.getLabel());
                return;
            }

            Map<Integer, String> wMaterialBatchNoMap =
                    wSFCStationMaterialList.stream().collect(Collectors.toMap(SFCStationMaterial::getMaterialID,
                            p -> p.BatchNumber));

            WMSAutoUtils.getInstance().WMS_AutoOutStock(wLoginUser, wAPSTaskPartPointDay, wMaterialBatchNoMap,
                    wSFCTaskSelf.StationID, wSFCTaskReport.ActNum, wErrorCode);

            //查找工件记录
            wParams.clear();
            wParams.put("TaskReportID", wSFCTaskReport.ID);
            List<SFCTaskIPT> wSFCTaskIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            //查询本道工序
            wParams.clear();
            wParams.put("ID", wSFCTaskReport.RoutePartPointID);
            FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                log.error(ExceptionTypes.Logic.getLabel());
                return;
            }

            //获取后工序
            FPCRoutePartPoint wNextRoutePartPoint = GetNextRoutePartPoint(wLoginUser, wFPCRoutePartPoint,
                    wSFCTaskReport, wErrorCode);
            if (wNextRoutePartPoint == null || wNextRoutePartPoint.ID <= 0) {
                if (wSFCTaskIPTList != null && wSFCTaskIPTList.size() > 0) {
                    List<String> wWorkPieceNoList =
                            wSFCTaskIPTList.stream().map(p -> p.ComponentCode).distinct().collect(Collectors.toList());
                    //②自动成品入库
                    WMSAutoUtils.getInstance().WMS_AutoInStock(wLoginUser, wAPSTaskPartPointDay, wSFCTaskSelf.StationID,
                            wWorkPieceNoList, wErrorCode);
                } else {
                    log.error(ExceptionTypes.Logic.getLabel());
                }
            } else {
                //半成品出库（看本道工序的报工模式）
                if (wFPCRoutePartPoint.ReportMode == SFCTaskReportMode.NUMBER_REPORT.getValue()) {
                    WMSAutoUtils.getInstance().WMS_AutoSemiOutStock(wLoginUser, wAPSTaskPartPointDay,
                            wSFCTaskReport.ActNum, wSFCTaskSelf.StationID, wErrorCode);
                } else {
                    if (wSFCTaskIPTList != null && wSFCTaskIPTList.size() > 0) {
                        List<String> wWorkPieceNoList =
                                wSFCTaskIPTList.stream().map(p -> p.ComponentCode).distinct().collect(Collectors.toList());
                        WMSAutoUtils.getInstance().WMS_AutoSemiOutStock(wLoginUser, wAPSTaskPartPointDay,
                                wWorkPieceNoList, wSFCTaskSelf.StationID, wErrorCode);
                    } else {
                        log.error(ExceptionTypes.Logic.getLabel());
                    }
                }
                //获取下道工序的日计划
                APSTaskPartPointDay wNextPointDay = GetNextPartPointDay(wLoginUser, wAPSTaskPartPointDay,
                        wNextRoutePartPoint, wErrorCode);
                if (wNextPointDay == null || wNextPointDay.ID <= 0) {
//                    log.error(ExceptionTypes.Logic.getLabel());
                    return;
                }
                //半成品入库
                if (wNextRoutePartPoint.ReportMode == SFCTaskReportMode.NUMBER_REPORT.getValue()) {
                    WMSAutoUtils.getInstance().WMS_AutoSemiInStock(wLoginUser, wNextPointDay, wSFCTaskReport.ActNum,
                            wSFCTaskSelf.StationID, wErrorCode);
                } else {
                    if (wSFCTaskIPTList != null && wSFCTaskIPTList.size() > 0) {
                        List<String> wWorkPieceNoList =
                                wSFCTaskIPTList.stream().map(p -> p.ComponentCode).distinct().collect(Collectors.toList());
                        WMSAutoUtils.getInstance().WMS_AutoSemiInStock(wLoginUser, wNextPointDay,
                                wSFCTaskSelf.StationID, wWorkPieceNoList, wErrorCode);
                    } else {
                        log.error(ExceptionTypes.Logic.getLabel());
                    }
                }
            }
        } catch (Exception ex) {
            log.error("StockDeduct", ex);
        }
    }

    /**
     * 获取下道工序的日计划
     */
    private APSTaskPartPointDay GetNextPartPointDay(BMSEmployee wLoginUser, APSTaskPartPointDay wAPSTaskPartPointDay,
                                                    FPCRoutePartPoint wNextRoutePartPoint,
                                                    OutResult<Integer> wErrorCode) {
        APSTaskPartPointDay wResult = new APSTaskPartPointDay();
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("OrderID", wAPSTaskPartPointDay.OrderID);
            wParams.put("RoutePartPointID", wNextRoutePartPoint.ID);

            Calendar wSTime = (Calendar) wAPSTaskPartPointDay.StationDate.clone();
            wSTime.set(wSTime.get(Calendar.YEAR), wSTime.get(Calendar.MONTH), wSTime.get(Calendar.DATE), 0, 0, 0);
            wSTime.add(Calendar.SECOND, -1);

            Calendar wETime = (Calendar) wAPSTaskPartPointDay.StationDate.clone();
            wETime.set(wSTime.get(Calendar.YEAR), wSTime.get(Calendar.MONTH), wSTime.get(Calendar.DATE), 23, 59, 59);

            wParams.put("StationDateStartTime", wSTime);
            wParams.put("StationDateEndTime", wETime);
            wResult = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
        } catch (Exception ex) {
            log.error("GetNextPartPointDay", ex);
        }
        return wResult;
    }

    /**
     * 获取后工序
     */
    private FPCRoutePartPoint GetNextRoutePartPoint(BMSEmployee wLoginUser, FPCRoutePartPoint wFPCRoutePartPoint,
                                                    SFCTaskReport wSFCTaskReport,
                                                    OutResult<Integer> wErrorCode) {
        FPCRoutePartPoint wResult = new FPCRoutePartPoint();
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("RouteID", wFPCRoutePartPoint.RouteID);
            wParams.put("Active", 1);
            List<FPCRoutePartPoint> wFPCRoutePartPointList = mFPCRoutePartPointService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wFPCRoutePartPointList == null || wFPCRoutePartPointList.size() <= 0) {
                log.error(ExceptionTypes.Logic.getLabel());
                return wResult;
            }

            //查找后工序
            List<FPCRoutePartPoint> wNextList =
                    wFPCRoutePartPointList.stream().filter(p -> p.PrevPartPointID == wFPCRoutePartPoint.ID
                            || wFPCRoutePartPoint.NextPartPointID.stream().anyMatch(q -> q == p.ID)).collect(Collectors.toList());
            if (wNextList != null && wNextList.size() > 0) {
                wResult = wNextList.get(0);
                return wResult;
            }
        } catch (Exception ex) {
            log.error("GetNextRoutePartPoint", ex);
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_BindingFrockBindRecord(BMSEmployee wLoginUser,
                                                             List<SFCFrockBindRecord> wSFCFrockBindRecordList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //获取工装类型
            Map<String, Object> wParams = new HashMap<String, Object>();

            DMSFrockLedger wDMSFrockLedger = mDMSFrockLedgerService.SelectPO(wLoginUser,
                    wSFCFrockBindRecordList.get(0).FrockLedgerID, "", wErrorCode);
            if (wDMSFrockLedger == null || wDMSFrockLedger.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            SFCWorkpieceRecord wSFCWorkpieceRecord;
            int wAffectRows;
            for (SFCFrockBindRecord wSFCFrockBindRecord : wSFCFrockBindRecordList) {
                //①验证工件码是否存在
                wParams.put("Code", wSFCFrockBindRecord.ComponentCode);
                wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams, wErrorCode);
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】未扫码自检!", wSFCFrockBindRecord.ComponentCode);
                    return wResult;
                }
                //②验证工件码是否绑定
                if (StringUtils.isNotEmpty(wSFCWorkpieceRecord.FrockNo)) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】已与工装【{1}】绑定!", wSFCFrockBindRecord.ComponentCode,
                            wSFCWorkpieceRecord.FrockNo);
                    return wResult;
                }
                //②验证工装类型和元件类型是否匹配
                if (wDMSFrockLedger.FrockType == AMSFrockType.JJ.getValue()
                        && wSFCWorkpieceRecord.ComponentType != PDMComponentTypes.Component.getValue()) {
                    //夹具只能绑定元件
                    wResult.FaultCode += StringUtils.Format("工装【{0}】的类型为【夹具】，只能绑定【元件】!",
                            wSFCFrockBindRecord.FrockLedgerCode);
                    return wResult;
                } else if (wDMSFrockLedger.FrockType == AMSFrockType.MJ.getValue()
                        && wSFCWorkpieceRecord.ComponentType != PDMComponentTypes.Core.getValue()) {
                    //模具只能绑定芯子
                    wResult.FaultCode += StringUtils.Format("工装【{0}】的类型为【模具】，只能绑定【芯子】!",
                            wSFCFrockBindRecord.FrockLedgerCode);
                    return wResult;
                }
                //③维护工件码的绑定工装
                wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                wSFCWorkpieceRecord.FrockNo = wSFCFrockBindRecord.FrockLedgerCode;
                wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
                //④维护工装绑定状态
                wSFCFrockBindRecord.setUserInfo(wLoginUser);
                wSFCFrockBindRecord.ComponentType = wSFCWorkpieceRecord.ComponentType;
                wSFCFrockBindRecord.Status = SFCFrockBindRecordStatus.BINDING.getValue();
                wAffectRows = mSFCFrockBindRecordService.UpdatePo(wLoginUser, wSFCFrockBindRecord, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_UnBindingFrockBindRecord(BMSEmployee wLoginUser,
                                                               List<SFCFrockBindRecord> wSFCFrockBindRecordList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            SFCWorkpieceRecord wSFCWorkpieceRecord;
            int wAffectRows;
            for (SFCFrockBindRecord wSFCFrockBindRecord : wSFCFrockBindRecordList) {
                //①修改绑定状态
                wSFCFrockBindRecord.setUserInfo(wLoginUser);
                wSFCFrockBindRecord.Status = SFCFrockBindRecordStatus.UNBINDING.getValue();
                wAffectRows = mSFCFrockBindRecordService.UpdatePo(wLoginUser, wSFCFrockBindRecord, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
                //②清除工件记录的绑定工装码
                wParams.put("Code", wSFCFrockBindRecord.ComponentCode);
                wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams, wErrorCode);
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】未扫码自检!", wSFCFrockBindRecord.ComponentCode);
                    return wResult;
                }

                wSFCWorkpieceRecord.FrockNo = "";
                wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_BoxReport(BMSEmployee wLoginUser,
                                                List<SFCWorkpieceRecord> wSFCWorkpieceRecordList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            SFCBoxReport wSFCBoxReport = new SFCBoxReport();
            wSFCBoxReport.setUserInfo(wLoginUser);
            wSFCBoxReport.OrderID = wSFCWorkpieceRecordList.get(0).OrderID;
            wSFCBoxReport.OrderNo = wSFCWorkpieceRecordList.get(0).OrderNo;
            wSFCBoxReport.ProductID = wSFCWorkpieceRecordList.get(0).ProductID;
            wSFCBoxReport.WorkpieceRecordIDList =
                    wSFCWorkpieceRecordList.stream().map(p -> p.ID).distinct().collect(Collectors.toList());
            int wAffectRows = mSFCBoxReportService.UpdatePo(wLoginUser, wSFCBoxReport, wErrorCode);
            if (wAffectRows > 0) {
                for (SFCWorkpieceRecord wSFCWorkpieceRecord : wSFCWorkpieceRecordList) {
                    wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                    wSFCWorkpieceRecord.Status = SFCTaskStatus.Reported.getValue();
                    wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
                    if (wAffectRows <= 0) {
                        wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                        return wResult;
                    }
                }
                Map<String, Object> wParams = new HashMap<String, Object>();
                wParams.put("ID", wSFCBoxReport.OrderID);
                OMSOrder wOMSOrder = mOMSOrderService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wOMSOrder != null && wOMSOrder.ID > 0) {
                    wOMSOrder.FQTYActual += wSFCWorkpieceRecordList.size();
                    wOMSOrder.setUserInfo(wLoginUser);
                    wAffectRows = mOMSOrderService.UpdatePo(wLoginUser, wOMSOrder, wErrorCode);
                    if (wAffectRows <= 0) {
                        wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                        return wResult;
                    }
                } else {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
            } else {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<SFCBoxReport> SFC_QueryBoxReportInfo(BMSEmployee wLoginUser, int wBoxReportID) {
        ServiceResult<SFCBoxReport> wResult = new ServiceResult<>();
        wResult.Result = new SFCBoxReport();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            wResult.Result = mSFCBoxReportService.SelectPO(wLoginUser, wBoxReportID, "", wErrorCode);
            if (wResult.Result.WorkpieceRecordIDList != null && wResult.Result.WorkpieceRecordIDList.size() > 0) {
                wResult.Result.WorkpieceRecordList = mSFCWorkpieceRecordService.SelectPOList(wLoginUser,
                        wResult.Result.WorkpieceRecordIDList, new HashMap<String, Object>(), Pagination.getNewMaxSize(),
                        wErrorCode);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<SFCTaskIPT>> SFC_QueryToSpecialTaskList(BMSEmployee wLoginUser, String wComponentCode) {
        ServiceResult<List<SFCTaskIPT>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ComponentCode", wComponentCode);
            wParams.put("Active", 1);
            wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
            wParams.put("Status", SFCTaskStatus.Done.getValue());
            wResult.Result = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryToSpecialTaskList", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_DeleteTaskIPTList(BMSEmployee wLoginUser, List<SFCTaskIPT> wSFCTaskIPTList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            mSFCTaskIPTService.DeletePOList(wLoginUser, wSFCTaskIPTList, wErrorCode);

            //删除互检、专检单
            Map<String, Object> wParams = new HashMap<String, Object>();
            for (SFCTaskIPT wSFCTaskIPT : wSFCTaskIPTList) {
                if (wSFCTaskIPT.Type != FPCCheckType.SELF_CHECK.getValue()) {
                    continue;
                }

                wParams.clear();
                wParams.put("OrderID", wSFCTaskIPT.OrderID);
                wParams.put("RoutePartID", wSFCTaskIPT.RoutePartID);
                wParams.put("RoutePartPointID", wSFCTaskIPT.RoutePartPointID);
                wParams.put("ComponentCode", wSFCTaskIPT.ComponentCode);
                List<SFCTaskIPT> sfcTaskIPTS = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                if (sfcTaskIPTS.size() > 0) {
                    mSFCTaskIPTService.DeletePOList(wLoginUser, sfcTaskIPTS, wErrorCode);
                }

                //查询工序日计划，重置首检状态
                wParams.clear();
                wParams.put("ID", wSFCTaskIPT.TaskPartPointDayID);
                APSTaskPartPointDay apsTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (apsTaskPartPointDay.FirstCheckStatus == APSFirstCheckStatus.Locked.getValue()) {
                    apsTaskPartPointDay.FirstCheckStatus = APSFirstCheckStatus.ToCheck.getValue();
                    mAPSTaskPartPointDayService.UpdatePo(wLoginUser, apsTaskPartPointDay, wErrorCode);
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_DeleteTaskIPT(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            mSFCTaskIPTService.DeletePO(wLoginUser, wSFCTaskIPT, wErrorCode);
            //查询工序日计划，重置首检状态
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCTaskIPT.TaskPartPointDayID);
            APSTaskPartPointDay apsTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (apsTaskPartPointDay.FirstCheckStatus == APSFirstCheckStatus.Locked.getValue()) {
                apsTaskPartPointDay.FirstCheckStatus = APSFirstCheckStatus.ToCheck.getValue();
                mAPSTaskPartPointDayService.UpdatePo(wLoginUser, apsTaskPartPointDay, wErrorCode);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<IPTValue>> SFC_QueryValueList(BMSEmployee wLoginUser, Map<String, Object> wParams,
                                                            Pagination wPagination) {
        ServiceResult<List<IPTValue>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            wResult.Result = mIPTValueService.SelectPOList(wLoginUser, wParams, wPagination,
                    wErrorCode);

            //查询工步信息
            if (wResult.Result != null && wResult.Result.size() > 0) {
                List<Integer> wRouteStepIDList =
                        wResult.Result.stream().map(p -> p.RouteStepID).distinct().collect(Collectors.toList());
                wParams.clear();
                List<FPCRouteStep> fpcRouteSteps = mFPCRouteStepService.SelectPOList(wLoginUser, wRouteStepIDList,
                        wParams, Pagination.getNewMaxSize(),
                        wErrorCode);
                Map<Integer, FPCRouteStep> wIDRouteStepMap =
                        fpcRouteSteps.stream().collect(Collectors.toMap(FPCRouteStep::getID
                                , p -> p));
                for (IPTValue wIPTValue : wResult.Result) {
                    if (wIDRouteStepMap.containsKey(wIPTValue.RouteStepID)) {
                        wIPTValue.RouteStep = wIDRouteStepMap.get(wIPTValue.RouteStepID);
                    }
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryValueList", e);
        }
        return wResult;
    }

    //endregion

    //region 开工检查

    @Override
    public ServiceResult<Integer> SFC_SubmitTaskStartCheckRecord(BMSEmployee wLoginUser,
                                                                 SFCTaskStartCheck wSFCTaskStartCheck) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            if (wLoginUser.DepartmentID <= 0) {
                wResult.FaultCode += StringUtils.Format("登录者【{0}】未设置部门，请联系系统管理员!", wLoginUser.Name);
                return wResult;
            }

            //①获取登录者所在班组
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("DepartmentID", wLoginUser.DepartmentID);
            BMSClassInfo wBMSClassInfo = mBMSClassInfoService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wBMSClassInfo == null || wBMSClassInfo.ID <= 0) {
                wResult.FaultCode += StringUtils.Format("未查询到登录者【{0}】所在班组，请联系系统管理员!", wLoginUser.Name);
                return wResult;
            }
            //②获取班组长
            List<Integer> wMonitorIDList = StringUtils.parseIntList(wBMSClassInfo.MonitorIDs.split(","));
            if (wMonitorIDList == null || wMonitorIDList.size() <= 0) {
                wResult.FaultCode += StringUtils.Format("班组【{0}】未设置班组长，请联系系统管理员!", wBMSClassInfo.DepartmentName);
                return wResult;
            }

            //①保存数据
            wSFCTaskStartCheck.setUserInfo(wLoginUser);
            int wAffectRows = mSFCTaskStartCheckService.UpdatePo(wLoginUser, wSFCTaskStartCheck, wErrorCode);
            if (wAffectRows <= 0) {
                wResult.FaultCode += "保存开工检查记录失败，请联系系统管理员!";
                return wResult;
            }

            //检查设备点检
            TaskTimerUtils.AddTask("CheckDevicePointCheck",
                    () -> CheckDevicePointCheck(wLoginUser, wSFCTaskStartCheck.TaskSelfID, wErrorCode)
            );

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 检查设备点检
     */
    @Override
    public void CheckDevicePointCheck(BMSEmployee wLoginUser, int wTaskSelfID, OutResult<Integer> wErrorCode) {
        try {
            //①查询工单
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wTaskSelfID);
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0) {
                log.error("CheckDevicePointCheck：工单异常");
                return;
            }
            //②查询工位
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.StationID);
            FMCStation wFMCStation = mFMCStationService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFMCStation == null || wFMCStation.ID <= 0) {
                log.error("CheckDevicePointCheck：工位异常");
                return;
            }
            if (wFMCStation.DeviceID == null || wFMCStation.DeviceID.size() != 1) {
                log.info(StringUtils.Format("CheckDevicePointCheck：{0}工位未配置设备或配置不止一台设备", wFMCStation.Name));
                return;
            }
            //查询设备编码
            wParams.clear();
            wParams.put("ID", wFMCStation.DeviceID.get(0));
            DMSDeviceLedger wDMSDeviceLedger = mDMSDeviceLedgerService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wDMSDeviceLedger == null || wDMSDeviceLedger.ID <= 0) {
                log.error("CheckDevicePointCheck：设备异常");
                return;
            }
            //③查询设备点检记录（日常点检）
            List<DMSMaintenancePlan> wDMSMaintenancePlanList =
                    DRMServiceImpl.getInstance().DMS_QueryDMSMaintenancePlanList(wLoginUser
                            , wDMSDeviceLedger.Code, "", 0, -1,
                            -1, -1, 1, Pagination.getNewMaxSize()).Result;
            //④若执行进度不是100%，则工位报警
            if (wDMSMaintenancePlanList == null || wDMSMaintenancePlanList.size() <= 0
                    || wDMSMaintenancePlanList.get(0).FQTY != wDMSMaintenancePlanList.get(0).Total || wDMSMaintenancePlanList.get(0).Total <= 0) {
                wFMCStation.Status = 1;
                wFMCStation.AlarmText = StringUtils.Format("设备【{0}{1}】未完成日常点检!", wDMSDeviceLedger.Code,
                        wDMSDeviceLedger.Name);
                mFMCStationService.UpdatePo(wLoginUser, wFMCStation, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("CheckDevicePointCheck", ex);
        }
    }

    @Override
    public ServiceResult<Integer> SFC_SFCTaskSelfParamPrevCheck(BMSEmployee wLoginUser,
                                                                SFCTaskSelfParam wSFCTaskSelfParam) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCTaskSelfParam.TaskSelfID);
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.Logic.getLabel();
                return wResult;
            }

            wResult.CustomResult.put("SFCTaskSelf", wSFCTaskSelf);

            //首检状态检查
            if (wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.Locked.getValue()
                    || wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.CheckBad.getValue()) {
                wResult.FaultCode = StringUtils.Format("工单【{0}】，订单【{1}】，型号【{2}】首检状态为`{3}`，禁止生产!", wSFCTaskSelf.Code,
                        wSFCTaskSelf.OrderNo, wSFCTaskSelf.ProductCode,
                        APSFirstCheckStatus.getEnumType(wSFCTaskSelf.FirstCheckStatus).getLabel());
                return wResult;
            }

            //判断该工序是否已停序
            if (EXCServiceImpl.getInstance().EXC_IsRoutePartPointStop(wLoginUser, wSFCTaskSelf.OrderID,
                    wSFCTaskSelf.RoutePartPointID).Result) {
                wResult.FaultCode += StringUtils.Format("工序【{0}】已停序!!!", wSFCTaskSelf.RoutePartPointName);
                return wResult;
            }

            //验证工位工序实时物料是否满足
            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            wParams.put("IsCodeScanning", 1);
            List<MSSBOMItem> wMSSBOMItemList = mMSSBOMItemService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            if (wMSSBOMItemList != null && wMSSBOMItemList.size() > 0) {
                wParams.clear();
                wParams.put("StationID", wSFCTaskSelf.StationID);
                wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                List<SFCStationMaterial> wSFCStationMaterialList = mSFCStationMaterialService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(), wErrorCode);
                if (wMSSBOMItemList.stream().anyMatch(p -> wSFCStationMaterialList.stream().noneMatch(q -> q.MaterialID == p.MaterialID))) {
                    MSSBOMItem wMSSBOMItem =
                            wMSSBOMItemList.stream().filter(p -> wSFCStationMaterialList.stream().noneMatch(q -> q.MaterialID == p.MaterialID)).findFirst().get();
                    wResult.FaultCode += StringUtils.Format("未查询到【{0}-{1}】物料批次，请及时上料!",
                            wMSSBOMItem.MaterialNo, wMSSBOMItem.MaterialName);
                    return wResult;
                }
            }

            //条码转换
            //①查询工位信息
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.StationID);
            FMCStation wFMCStation = mFMCStationService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFMCStation == null || wFMCStation.ID <= 0) {
                wResult.FaultCode += StringUtils.Format("未查询到工位信息，请联系系统管理员!");
                return wResult;
            }

            //如果为首工序，无检查工件码
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.RoutePartPointID);
            FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            if (StringUtils.isEmpty(wSFCTaskSelfParam.WorkpieceNo) && wFPCRoutePartPoint.PrevPartPointID <= 0) {
                return wResult;
            }

            if (StringUtils.isEmpty(wSFCTaskSelfParam.WorkpieceNo)) {
                wResult.FaultCode += StringUtils.Format("非首工序工件条码不能为空!");
                return wResult;
            }

            //②热定型类，检查是否为托盘码，检查托盘码是否已绑定工单；
            if (wFMCStation.BarCodeType == FMCBarCodeType.Heat.getValue()) {
                if (StringUtils.isEmpty(wSFCTaskSelfParam.WorkpieceNo)) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工位设置的条码类型为`热定型类`，请提供有效的托盘码!", wFMCStation.Name);
                    return wResult;
                }
                //查询托盘码
                wParams.clear();
                wParams.put("CodeEQ", wSFCTaskSelfParam.WorkpieceNo);
                DMSFrockLedger wDMSFrockLedger = mDMSFrockLedgerService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wDMSFrockLedger == null || wDMSFrockLedger.ID <= 0 || wDMSFrockLedger.FrockType != AMSFrockType.TP.getValue()) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工位设置的条码类型为`热定型类`，请提供有效的托盘码!", wFMCStation.Name);
                    return wResult;
                }
                //查询托盘绑定工单记录
                wParams.clear();
                wParams.put("FrockLedgerCode", wDMSFrockLedger.Code);
                wParams.put("Status", SFCFrockBindRecordStatus.BINDING.getValue());
                wParams.put("ComponentType", PDMComponentTypes.WorkOrder.getValue());
                List<SFCFrockBindRecord> sfcFrockBindRecords = mSFCFrockBindRecordService.SelectPOList(wLoginUser,
                        wParams,
                        wErrorCode);
                if (sfcFrockBindRecords == null || sfcFrockBindRecords.size() <= 0) {
                    wResult.FaultCode += StringUtils.Format("托盘码【{0}】未绑定工单!", wSFCTaskSelfParam.WorkpieceNo);
                    return wResult;
                }

                return wResult;
            }
            //③芯子→成品类，检查芯子与成品组装记录，转换为成品码；
            else if (wFMCStation.BarCodeType == FMCBarCodeType.Core.getValue()) {
                if (StringUtils.isEmpty(wSFCTaskSelfParam.WorkpieceNo)) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工位设置的条码类型为`芯子→成品`，请提供有效的芯子码!", wFMCStation.Name);
                    return wResult;
                }
                //查询工件记录，判断是否为芯子码
                wParams.clear();
                wParams.put("Code", wSFCTaskSelfParam.WorkpieceNo);
                SFCWorkpieceRecord sfcWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (sfcWorkpieceRecord == null || sfcWorkpieceRecord.ID <= 0 || sfcWorkpieceRecord.ComponentType != PDMComponentTypes.Core.getValue()) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工位设置的条码类型为`芯子→成品`，请提供有效的芯子码!", wFMCStation.Name);
                    return wResult;
                }
                //查询芯子与成品的组装记录
                wParams.clear();
                wParams.put("SourceCode", wSFCTaskSelfParam.WorkpieceNo);
                wParams.put("ComponentType", PDMComponentTypes.Core.getValue());
                wParams.put("TargetComponentType", PDMComponentTypes.Product.getValue());
                SFCAssemblyRecord sfcAssemblyRecord = mSFCAssemblyRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (sfcAssemblyRecord == null || sfcAssemblyRecord.ID <= 0) {
                    wResult.FaultCode += StringUtils.Format("芯子【{0}】未与成品组装，请先组装!", wFMCStation.Name);
                    return wResult;
                }

                wSFCTaskSelfParam.WorkpieceNo = sfcAssemblyRecord.TargetCode;
            }
            //④托盘→产品类，检查是否为托盘码，检查托盘与产品的绑定记录，转换为产品码；
            else if (wFMCStation.BarCodeType == FMCBarCodeType.Tray.getValue()) {
                if (StringUtils.isEmpty(wSFCTaskSelfParam.WorkpieceNo)) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工位设置的条码类型为`托盘→产品`，请提供有效的托盘码!", wFMCStation.Name);
                    return wResult;
                }
                //查询托盘码
                wParams.clear();
                wParams.put("CodeEQ", wSFCTaskSelfParam.WorkpieceNo);
                DMSFrockLedger wDMSFrockLedger = mDMSFrockLedgerService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wDMSFrockLedger == null || wDMSFrockLedger.ID <= 0 || wDMSFrockLedger.FrockType != AMSFrockType.TP.getValue()) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工位设置的条码类型为`托盘→产品`，请提供有效的托盘码!", wFMCStation.Name);
                    return wResult;
                }
                //根据托盘码查询成品码
                SFCWorkpieceRecord wSFCWorkpieceRecord =
                        SFCFrockBindServiceImpl.getInstance().SFC_QueryProductInfoByTray(wLoginUser,
                                wSFCTaskSelfParam.WorkpieceNo, "").Result;

                if (StringUtils.isEmpty(wSFCWorkpieceRecord.Code)) {
                    wResult.FaultCode += StringUtils.Format("托盘【{0}】未绑定产品，请先绑定!", wSFCTaskSelfParam.WorkpieceNo);
                    return wResult;
                }

                wSFCTaskSelfParam.WorkpieceNo = wSFCWorkpieceRecord.Code;
            }

            //验证工件订单与当前工单的订单是否一致
            wParams.clear();
            wParams.put("CodeEQ", wSFCTaskSelfParam.WorkpieceNo);
            SFCWorkpieceRecord sfcWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (sfcWorkpieceRecord != null && sfcWorkpieceRecord.ID > 0 && sfcWorkpieceRecord.OrderID != wSFCTaskSelf.OrderID) {
                wResult.FaultCode += StringUtils.Format("工件【{0}】绑定的订单【{1}】与当前工单【{2}】绑定的订单【{3}】不一致!",
                        wSFCTaskSelfParam.WorkpieceNo, sfcWorkpieceRecord.OrderNo, wSFCTaskSelf.Code,
                        wSFCTaskSelf.OrderNo);
                return wResult;
            }

            //验证工件是否为合格产品，若不合格报错
            if (sfcWorkpieceRecord != null && sfcWorkpieceRecord.ID > 0) {
                //查询本道工序是否有自检不合格记录，若没有则报错
                wParams.clear();
                wParams.put("OrderID", sfcWorkpieceRecord.OrderID);
                wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                wParams.put("ComponentCode", sfcWorkpieceRecord.Code);
                wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                List<SFCTaskIPT> sfcTaskIPTS = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                if (sfcTaskIPTS.size() <= 0 && sfcWorkpieceRecord.CheckResult == 2) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】前工序检验结果不合格!",
                            wSFCTaskSelfParam.WorkpieceNo);
                    return wResult;
                }

                //验证是否该工件已流转到后工序，若是，则报错
                wParams.clear();
                wParams.put("WorkpieceRecordID", sfcWorkpieceRecord.ID);
                List<SFCWorkpieceResume> sfcWorkpieceResumes = mSFCWorkpieceResumeService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(),
                        wErrorCode);
                if (sfcTaskIPTS.size() > 0 && sfcWorkpieceResumes.size() > 0) {
                    //按照ID倒序排序
                    sfcWorkpieceResumes.sort(Comparator.comparing(SFCWorkpieceResume::getID,
                            Comparator.reverseOrder()));
                    if (sfcWorkpieceResumes.get(0).RoutePartPointID != wSFCTaskSelf.RoutePartPointID) {
                        wResult.FaultCode += StringUtils.Format("工件【{0}】已流转到后工序【{1}】!",
                                wSFCTaskSelfParam.WorkpieceNo, sfcWorkpieceResumes.get(0).RoutePartPointName);
                        return wResult;
                    }
                }
            }

            //验证该工件的前工序是否已做自检任务，若没有，则报错提示
            if (wFPCRoutePartPoint.PrevPartPointID > 0) {
                //验证工件记录是否存在
                wParams.clear();
                wParams.put("CodeEQ", wSFCTaskSelfParam.WorkpieceNo);
                SFCWorkpieceRecord sfcWorkpieceRecord1 = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (sfcWorkpieceRecord1 == null || sfcWorkpieceRecord1.ID <= 0) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】记录不存在!", wSFCTaskSelfParam.WorkpieceNo);
                    return wResult;
                }

                //前工艺工序
                wParams.clear();
                wParams.put("ID", wFPCRoutePartPoint.PrevPartPointID);
                FPCRoutePartPoint wPrevRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wPrevRoutePartPoint == null || wPrevRoutePartPoint.ID <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }

                //判断前后工序的产出类型是否相同，若相同，则判断是否坐过自检，若不相同，则判断是否已组装
                if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType) {
                    //前工序自检单
                    wParams.clear();
                    wParams.put("OrderID", wSFCTaskSelf.OrderID);
                    wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                    wParams.put("ComponentCode", wSFCTaskSelfParam.WorkpieceNo);
                    wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                    wParams.put("Status", SFCTaskStatus.Done.getValue());
                    SFCTaskIPT wPrevSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
                    if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                            && (wPrevSFCTaskIPT == null || wPrevSFCTaskIPT.ID <= 0)
                            && wPrevRoutePartPoint.ReportMode != SFCTaskReportMode.NUMBER_REPORT.getValue()) {
                        wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的自检!", wSFCTaskSelfParam.WorkpieceNo,
                                wFPCRoutePartPoint.PrevPartPointName);
                        return wResult;
                    }
                    //验证该工件的前工序是否报工
                    if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                            && wPrevSFCTaskIPT.IsReported != 1
                            && wPrevRoutePartPoint.ReportMode != SFCTaskReportMode.NUMBER_REPORT.getValue()) {
                        wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的报工或报工确认!",
                                wSFCTaskSelfParam.WorkpieceNo,
                                wFPCRoutePartPoint.PrevPartPointName);
                        return wResult;
                    }
                    //验证该工件的前工序是否设置的强制做专检，且该工件在前工序已做专检，否则报错提示
                    if (wPrevRoutePartPoint.IsSpecialCheck == 1) {
                        wParams.clear();
                        wParams.put("OrderID", wSFCTaskSelf.OrderID);
                        wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                        wParams.put("ComponentCode", wSFCTaskSelfParam.WorkpieceNo);
                        wParams.put("Type", FPCCheckType.SPECIAL_CHECK.getValue());
                        wParams.put("Status", SFCTaskStatus.Done.getValue());
                        SFCTaskIPT wPrevSpecialTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
                        if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                                && (wPrevSpecialTaskIPT == null || wPrevSpecialTaskIPT.ID <= 0)
                                && wPrevRoutePartPoint.ReportMode != SFCTaskReportMode.NUMBER_REPORT.getValue()) {
                            wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的专检!",
                                    wSFCTaskSelfParam.WorkpieceNo,
                                    wFPCRoutePartPoint.PrevPartPointName);
                            return wResult;
                        }
                    }
                } else {
                    //检查组装记录是否存在，若存在，则开工检验失败
                    if (sfcWorkpieceRecord1.ComponentType == wPrevRoutePartPoint.ComponentType) {
                        wParams.clear();
                        wParams.put("ComponentType", wPrevRoutePartPoint.ComponentType);
                        wParams.put("TargetComponentType", wFPCRoutePartPoint.ComponentType);
                        wParams.put("SourceCode", wSFCTaskSelfParam.WorkpieceNo);
                        List<SFCAssemblyRecord> sfcAssemblyRecords =
                                mSFCAssemblyRecordService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                                        wErrorCode);
                        if (sfcAssemblyRecords != null && sfcAssemblyRecords.size() > 0) {
                            wResult.FaultCode += StringUtils.Format("工件【{0}】已组装，开工检验失败!",
                                    wSFCTaskSelfParam.WorkpieceNo);
                            return wResult;
                        }
                    }
                    //若检验码的类型与前道工序产出类型一致，则检查前道工序是否已做自检
                    if (sfcWorkpieceRecord1.ComponentType == wPrevRoutePartPoint.ComponentType) {
                        wParams.clear();
                        wParams.put("OrderID", wSFCTaskSelf.OrderID);
                        wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                        wParams.put("ComponentCode", wSFCTaskSelfParam.WorkpieceNo);
                        wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                        wParams.put("Status", SFCTaskStatus.Done.getValue());
                        SFCTaskIPT wPrevSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
                        if ((wPrevSFCTaskIPT == null || wPrevSFCTaskIPT.ID <= 0)
                                && wPrevRoutePartPoint.ReportMode != SFCTaskReportMode.NUMBER_REPORT.getValue()) {
                            wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的自检!",
                                    wSFCTaskSelfParam.WorkpieceNo,
                                    wFPCRoutePartPoint.PrevPartPointName);
                            return wResult;
                        }
                    }
                }

                //无壳注胶线上料站首检，上料站不做首检或者首检不合格时预热站扫托盘码做工件开工检查不通过
                if (wFMCStation.BarCodeType == FMCBarCodeType.Tray.getValue()) {
                    //前工序自检单
                    wParams.clear();
                    wParams.put("OrderID", wSFCTaskSelf.OrderID);
                    wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                    wParams.put("ComponentCode", wSFCTaskSelfParam.WorkpieceNo);
                    wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                    wParams.put("Status", SFCTaskStatus.Done.getValue());
                    SFCTaskIPT sfcTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (sfcTaskIPT != null && sfcTaskIPT.ID > 0) {
                        wParams.clear();
                        wParams.put("ID", sfcTaskIPT.TaskPartPointDayID);
                        APSTaskPartPointDay apsTaskPartPointDay = mAPSTaskPartPointDayService.SelectPO(wLoginUser,
                                wParams,
                                wErrorCode);
                        if (apsTaskPartPointDay != null && apsTaskPartPointDay.ID > 0
                                && apsTaskPartPointDay.FirstCheckStatus != APSFirstCheckStatus.Default.getValue()
                                && apsTaskPartPointDay.FirstCheckStatus != APSFirstCheckStatus.CheckGood.getValue()) {
                            wResult.FaultCode += StringUtils.Format("前工序【{0}】未首检或首检不合格!",
                                    apsTaskPartPointDay.RoutePartPointName);
                            return wResult;
                        }
                    }
                }

                //验证前工序任务的报工数量，若前工序的报工数量小于本工序已报工数量，则自动发起异常
                String wMsg = CheckPrevReportTask(wLoginUser, wPrevRoutePartPoint, wFPCRoutePartPoint, wSFCTaskSelf,
                        wErrorCode);
                if (StringUtils.isNotEmpty(wMsg)) {
                    //自动发起异常流程
                    TaskTimerUtils.AddTask("AutoSendExcFlow",
                            () -> AutoSendExcFlow(wLoginUser, wPrevRoutePartPoint, wSFCTaskSelf, wMsg, wErrorCode)
                    );
                    //若工位做卡控，则报错提示
                    if (wFMCStation.IsRepockCheck == 1) {
                        wResult.FaultCode = StringUtils.Format("工件开工检查失败，上道工序【{0}】未报工!", wPrevRoutePartPoint.Name);
                        return wResult;
                    }
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 自动发起异常流程
     */
    private void AutoSendExcFlow(BMSEmployee wLoginUser, FPCRoutePartPoint wPrevRoutePartPoint,
                                 SFCTaskSelf wSFCTaskSelf, String wMsg, OutResult<Integer> wErrorCode) {
        try {
            //①查询上道工序的工单列表
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("OrderID", wSFCTaskSelf.OrderID);
            wParams.put("RoutePartPointID", wPrevRoutePartPoint);
            wParams.put("Active", 1);
            List<SFCTaskSelf> sfcTaskSelves = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (sfcTaskSelves == null || sfcTaskSelves.size() == 0) {
                return;
            }
            //②自动发起异常流程
            EXCServiceImpl.getInstance().EXC_AutoSendReportExc(wLoginUser, sfcTaskSelves.get(0).WorkerID,
                    sfcTaskSelves.get(0).StationID,
                    wMsg);
        } catch (Exception ex) {
            log.error("AutoSendExcFlow", ex);
        }
    }

    /**
     * 检查前工序是否报工
     */
    private String CheckPrevReportTask(BMSEmployee wLoginUser, FPCRoutePartPoint wPrevRoutePartPoint,
                                       FPCRoutePartPoint wFPCRoutePartPoint, SFCTaskSelf wSFCTaskSelf,
                                       OutResult<Integer> wErrorCode) {
        String wResult = "";
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("OrderID", wSFCTaskSelf.OrderID);
            wParams.put("RoutePartPointID", wFPCRoutePartPoint.ID);
            wParams.put("Active", 1);
            APSTaskPartPoint apsTaskPartPoint = mAPSTaskPartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (apsTaskPartPoint == null || apsTaskPartPoint.ID <= 0) {
                return wResult;
            }

            wParams.clear();
            wParams.put("OrderID", wSFCTaskSelf.OrderID);
            wParams.put("RoutePartPointID", wPrevRoutePartPoint.ID);
            wParams.put("Active", 1);
            APSTaskPartPoint apsTaskPartPoint1 = mAPSTaskPartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (apsTaskPartPoint1 == null || apsTaskPartPoint1.ID <= 0) {
                return wResult;
            }

            if (apsTaskPartPoint.ActNum > apsTaskPartPoint1.ActNum) {
                wResult = StringUtils.Format("订单【{0}】-工艺【{1}】-工序【{2}】未报工，请及时报工!", wSFCTaskSelf.OrderNo,
                        apsTaskPartPoint1.RoutePartName, apsTaskPartPoint1.RoutePartPointName);
                return wResult;
            }
        } catch (Exception ex) {
            log.error("CheckPrevReportTask", ex);
        }
        return wResult;
    }

    //endregion

    //region 计划相关
    @Override
    public ServiceResult<Integer> APS_IssueDayPlanList(BMSEmployee wLoginUser,
                                                       List<APSTaskPartPointDay> wAPSTaskPartPointDayList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //限制逻辑：下达数<=订单计划数*系统参数比例-历史已完成数；
            String wMsg = APS_RestrictionLogic(wLoginUser, wAPSTaskPartPointDayList, wErrorCode);
            if (StringUtils.isNotEmpty(wMsg)) {
                wResult.FaultCode += wMsg;
                return wResult;
            }

            //首检状态维护
            List<Integer> wRoutePartPointIDList =
                    wAPSTaskPartPointDayList.stream().map(p -> p.RoutePartPointID).distinct().collect(Collectors.toList());
            Map<Integer, Boolean> wFirstCheckStatusMap =
                    APSTaskPartPointDayDAO.getInstance().APS_GetFirstCheckStatusMap(wLoginUser, wRoutePartPointIDList,
                            wErrorCode);

            int wAffectRows = 0;
            for (APSTaskPartPointDay wAPSTaskPartPointDay : wAPSTaskPartPointDayList) {
                if (wFirstCheckStatusMap.containsKey(wAPSTaskPartPointDay.RoutePartPointID)) {
                    Boolean wNeedFirstCheck = wFirstCheckStatusMap.get(wAPSTaskPartPointDay.RoutePartPointID);
                    if (wNeedFirstCheck) {
                        wAPSTaskPartPointDay.FirstCheckStatus = APSFirstCheckStatus.ToCheck.getValue();
                    } else {
                        wAPSTaskPartPointDay.FirstCheckStatus = APSFirstCheckStatus.Default.getValue();
                    }
                } else {
                    wAPSTaskPartPointDay.FirstCheckStatus = APSFirstCheckStatus.Default.getValue();
                }

                wAPSTaskPartPointDay.Status = APSTaskStatus.Issued.getValue();
                wAPSTaskPartPointDay.setUserInfo(wLoginUser);
                wAffectRows = mAPSTaskPartPointDayService.UpdatePo(wLoginUser, wAPSTaskPartPointDay, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
                //触发自动派工逻辑
                TriggerAutoSFC(wLoginUser, wAPSTaskPartPointDay, wErrorCode);
            }

            //生成物料配送计划
            Calendar wStartDate =
                    wAPSTaskPartPointDayList.stream().min(Comparator.comparing(APSTaskPartPointDay::getStationDate)).get().StationDate;
            Calendar wEndDate =
                    wAPSTaskPartPointDayList.stream().max(Comparator.comparing(APSTaskPartPointDay::getStationDate)).get().StationDate;
            MRPServiceImpl.getInstance().MRP_CreateDelivery(wLoginUser, wStartDate, wEndDate);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 限制逻辑：下达数<=订单计划数*系统参数比例-历史已完成数；
     */
    private String APS_RestrictionLogic(BMSEmployee wLoginUser, List<APSTaskPartPointDay> wAPSTaskPartPointDayList,
                                        OutResult<Integer> wErrorCode) {
        String wResult = "";
        try {
            if (wAPSTaskPartPointDayList == null || wAPSTaskPartPointDayList.size() == 0) {
                return wResult;
            }

            List<Integer> wIDList =
                    wAPSTaskPartPointDayList.stream().map(p -> p.TaskPartPointID).distinct().collect(Collectors.toList());
            Map<String, Object> wParams = new HashMap<String, Object>();
            List<APSTaskPartPoint> wAPSTaskPartPointList = mAPSTaskPartPointService.SelectPOList(wLoginUser, wIDList,
                    wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);

            Map<Integer, List<APSTaskPartPointDay>> wTaskPartIDTaskPartPointMap = wAPSTaskPartPointDayList.stream()
                    .collect(Collectors.groupingBy(APSTaskPartPointDay::getTaskPartPointID));

            //系统比例参数
            double wDoubleValue = 1 +
                    StringUtils.parseDouble(BFCSystemConfigDAO.getInstance().BFC_GetValueByName(wLoginUser, -1,
                            "Sys_Exceed_Proportion", wErrorCode));

            for (APSTaskPartPoint wAPSTaskPartPoint : wAPSTaskPartPointList) {
                List<APSTaskPartPointDay> wDayTaskList = wTaskPartIDTaskPartPointMap.get(wAPSTaskPartPoint.ID);
                //订单计划数*系统比例参数
                float wExceedFQTY = (float) (wAPSTaskPartPoint.PlanNum * wDoubleValue);
                //历史已完成数
                //下达总数
                float wIssueFQTY = (float) wDayTaskList.stream().mapToDouble(p -> p.PlantNum).sum();
                if (wIssueFQTY <= wExceedFQTY - wAPSTaskPartPoint.HisActNum) {
                } else {
                    wResult = StringUtils.Format("日计划下达失败：【{0}】-【{1}】-【{2}】下达数[{3}]应<=订单计划数*系统参数比例[{4}]-历史已完成数[{5}]",
                            wAPSTaskPartPoint.OrderNo, wAPSTaskPartPoint.RoutePartName,
                            wAPSTaskPartPoint.RoutePartPointName, wIssueFQTY, wExceedFQTY,
                            wAPSTaskPartPoint.HisActNum);
                    return wResult;
                }
            }
        } catch (Exception e) {
            log.error("APS_RestrictionLogic", e);
        }
        return wResult;
    }

    /**
     * 自动派工（下达日计划）
     */
    private void TriggerAutoSFC(BMSEmployee wLoginUser, APSTaskPartPointDay wAPSTaskPartPointDay,
                                OutResult<Integer> wErrorCode) {
        try {
            //①根据班次查询班组管理范围
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ShiftType", wAPSTaskPartPointDay.ShiftType);
            wParams.put("StationActive", 1);
            wParams.put("ClassActive", 1);
            List<BMSClassRange> wBMSClassRangeList = mBMSClassRangeService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wBMSClassRangeList == null || wBMSClassRangeList.size() <= 0) {
                return;
            }
            //②根据工位的管理范围
            wBMSClassRangeList =
                    wBMSClassRangeList.stream().filter(p -> wAPSTaskPartPointDay.StationID.stream().anyMatch(q -> q == p.StationID)).collect(Collectors.toList());
            if (wBMSClassRangeList == null || wBMSClassRangeList.size() <= 0) {
                return;
            }
            //③遍历判断是否值班，若值班，则自动派工；若不值班，则自动保存派工人员
            boolean wIsAutoSFC = false;
            List<SFCTaskSelf> wSFCTaskSelfList = new ArrayList<>();
            SFCTaskSelf wSFCTaskSelf;
            for (BMSClassRange wBMSClassRange : wBMSClassRangeList) {
                if (wBMSClassRange.PersonIDList == null || wBMSClassRange.PersonIDList.size() <= 0) {
                    continue;
                }

                if (wBMSClassRange.AutoSFC == 1) {
                    wIsAutoSFC = true;
                }

                for (int wUserID : wBMSClassRange.PersonIDList) {
                    wSFCTaskSelf = new SFCTaskSelf();
                    wSFCTaskSelf.setUserInfo(wLoginUser);
                    wSFCTaskSelf.OrderID = wAPSTaskPartPointDay.OrderID;
                    wSFCTaskSelf.OrderNo = wAPSTaskPartPointDay.OrderNo;
                    wSFCTaskSelf.RoutePartID = wAPSTaskPartPointDay.RoutePartID;
                    wSFCTaskSelf.RoutePartPointID = wAPSTaskPartPointDay.RoutePartPointID;
                    wSFCTaskSelf.ProductID = wAPSTaskPartPointDay.ProductID;
                    wSFCTaskSelf.TaskPartID = wAPSTaskPartPointDay.TaskPartID;
                    wSFCTaskSelf.TaskPartPointID = wAPSTaskPartPointDay.TaskPartPointID;
                    wSFCTaskSelf.TaskPartPointDayID = wAPSTaskPartPointDay.ID;
                    wSFCTaskSelf.WorkShopID = wAPSTaskPartPointDay.WorkShopID;
                    wSFCTaskSelf.WorkAreaID = wAPSTaskPartPointDay.WorkAreaID;
                    wSFCTaskSelf.StationID = wBMSClassRange.StationID;
                    wSFCTaskSelf.StationDate = wAPSTaskPartPointDay.StationDate;
                    wSFCTaskSelf.ShiftType = wAPSTaskPartPointDay.ShiftType;
                    wSFCTaskSelf.PlantNum = wAPSTaskPartPointDay.PlantNum;
                    wSFCTaskSelf.ActNum = 0;
                    wSFCTaskSelf.WorkerID = wUserID;
                    wSFCTaskSelf.Status = SFCTaskStatus.Default.getValue();
                    wSFCTaskSelf.Active = 1;
                    wSFCTaskSelfList.add(wSFCTaskSelf);
                }
            }

            //设置了值班，自动派工
            if (wIsAutoSFC) {
                SFCServiceImpl.getInstance().SFC_SaveDispatchResult(wLoginUser, wSFCTaskSelfList);
            } else {//未设置，保存派工人员
                for (SFCTaskSelf wItemSelf : wSFCTaskSelfList) {
                    mSFCTaskSelfService.UpdatePo(wLoginUser, wItemSelf, wErrorCode);
                }
            }
        } catch (Exception ex) {
            log.error("TriggerAutoSFC", ex);
        }
    }
    //endregion

    //region 工控机

    @Override
    public ServiceResult<Integer> SFC_QueryMainInterfaceDataAll(BMSEmployee wLoginUser, String ipAddress,
                                                                int wShiftType) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            //①获取当前工位配置
            wParams.put("IPAddress", ipAddress);
            wParams.put("Active", 1);
            FMCIPConfig wFMCIPConfig = mFMCIPConfigService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFMCIPConfig == null || wFMCIPConfig.ID <= 0 || wFMCIPConfig.StationIDList == null || wFMCIPConfig.StationIDList.size() <= 0) {
                wResult.FaultCode += StringUtils.Format("当前工控机（IP：{0}）未配置工位列表!", ipAddress);
                return wResult;
            }
            wResult.CustomResult.put("FMCIPConfig", wFMCIPConfig);
            //①返回工位信息
            wParams.clear();
            List<FMCStation> wFMCStationList = mFMCStationService.SelectPOList(wLoginUser, wFMCIPConfig.StationIDList,
                    wParams, Pagination.getNewMaxSize(), wErrorCode);
            //翻译设备
            Map<Integer, List<DMSDeviceLedger>> wStationDeviceListMap = AsignDeviceName(wLoginUser, wFMCStationList,
                    wErrorCode);

            wResult.CustomResult.put("FMCStationList", wFMCStationList);
            wResult.CustomResult.put("FMCStationDeviceMap", wStationDeviceListMap);
            //②获取扫码枪配置
            wParams.clear();
            wParams.put("StationID", StringUtils.Join(",", wFMCIPConfig.StationIDList));
            wParams.put("Active", 1);
            List<FMCCodeScanningGunConfig> wFMCCodeScanningGunConfigList =
                    mFMCCodeScanningGunConfigService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
            wResult.CustomResult.put("FMCCodeScanningGunConfigList", wFMCCodeScanningGunConfigList);
            //④获取当前班次信息
            List<FMCShift> wFMCShiftList =
                    FMCServiceImpl.getInstance().FMC_QueryShiftList(wLoginUser, 9, 0, 0, 0,
                            Pagination.getNewMaxSize()).Result;
            if (wFMCShiftList == null || wFMCShiftList.size() <= 0) {
                wResult.FaultCode += "未查询到工厂班次设置，请联系系统管理员!";
                return wResult;
            }

            int wYear = Calendar.getInstance().get(Calendar.YEAR);
            int wMonth = Calendar.getInstance().get(Calendar.MONTH);
            int wDate = Calendar.getInstance().get(Calendar.DATE);

            FMCShift wCurrentShift = new FMCShift();
            if (wShiftType > 0) {
                if (wFMCShiftList.stream().noneMatch(p -> p.ShiftType == wShiftType)) {
                    wResult.FaultCode += "工厂班次设置中不存在所给班次，请联系系统管理员!";
                    return wResult;
                }
                wCurrentShift = wFMCShiftList.stream().filter(p -> p.ShiftType == wShiftType).findFirst().get();
            } else {
                for (FMCShift wFMCShift : wFMCShiftList) {
                    wFMCShift.StartTime.set(wYear, wMonth, wDate);
                    wFMCShift.EndTime.set(wYear, wMonth, wDate);
                    if (Calendar.getInstance().compareTo(wFMCShift.StartTime) >= 0 && Calendar.getInstance().compareTo(wFMCShift.EndTime) <= 0) {
                        wCurrentShift = wFMCShift;
                    }
                }
            }
            if (wCurrentShift.ID <= 0) {
                wResult.FaultCode += "未查询到当前班次信息，请检查班次设置!";
                return wResult;
            }
            wResult.CustomResult.put("CurrentShift", wCurrentShift);
            //③获取停线停序单
            wParams.clear();
            wParams.put("IsShowMessage", 1);
            List<EXCLineStop> wEXCLineStopList = mEXCLineStopService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);

            wParams.clear();
            wParams.put("Status", "2,5");
            wParams.put("IsShowMessage", 0);
            wEXCLineStopList.addAll(mEXCLineStopService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode));

            //③获取当前派工任务
            Calendar wSTime = Calendar.getInstance();
            wSTime.set(wSTime.get(Calendar.YEAR), wSTime.get(Calendar.MONTH), wSTime.get(Calendar.DATE), 0, 0, 0);
            wSTime.add(Calendar.SECOND, -1);

            Calendar wETime = Calendar.getInstance();
            wETime.set(wETime.get(Calendar.YEAR), wETime.get(Calendar.MONTH), wETime.get(Calendar.DATE), 23, 59, 59);
            wParams.clear();
            wParams.put("StationID", StringUtils.Join(",", wFMCIPConfig.StationIDList));
            wParams.put("ShiftType", wCurrentShift.ShiftType);
            wParams.put("Status", SFCTaskStatus.Doing.getValue());
            wParams.put("Active", 1);
            wParams.put("WorkerID", wLoginUser.ID);
            List<SFCTaskSelf> wCurrentTaskList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);

            //维护当前任务产出数量
            MaintenanceOutputQuantity(wLoginUser, wCurrentTaskList, wErrorCode);

            //停线停序原因赋值
            for (SFCTaskSelf wSFCTaskSelf : wCurrentTaskList) {
                if (wEXCLineStopList.stream().anyMatch(p -> p.OrderID == wSFCTaskSelf.OrderID
                        && p.PartPointList.stream().anyMatch(q -> q.RoutePartPointID == wSFCTaskSelf.RoutePartPointID))) {
                    wSFCTaskSelf.EXCLineStop = wEXCLineStopList.stream().filter(p -> p.OrderID == wSFCTaskSelf.OrderID
                            && p.PartPointList.stream().anyMatch(q -> q.RoutePartPointID == wSFCTaskSelf.RoutePartPointID)).findFirst().get();
                    wSFCTaskSelf.StopReason = wSFCTaskSelf.EXCLineStop.Remark;
                }
            }
            wResult.CustomResult.put("CurrentTaskList", wCurrentTaskList);
            //⑤获取当日计划、当日实际、当班计划、当班实际
            wParams.clear();
            wParams.put("StationID", StringUtils.Join(",", wFMCIPConfig.StationIDList));
            wParams.put("StaStartTime", wSTime);
            wParams.put("StaEndTime", wETime);
            wParams.put("ShiftType", wCurrentShift.ShiftType);
            wParams.put("Active", 1);
            wParams.put("WorkerID", wLoginUser.ID);
            wParams.put("Status", "1,2,3,4,5");
            wParams.put("IsRepairPlan", 0);
            List<SFCTaskSelf> wSFCTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            //获取历史已开工的工单任务列表
            List<SFCTaskSelf> wHistoryTaskSelfList =
                    SFCFrockBindServiceImpl.getInstance().SFC_QuerHistoryStartTaskSelfList(wLoginUser,
                            wCurrentShift.ShiftType, wFMCIPConfig.StationIDList, 0).Result;
            wSFCTaskSelfList.addAll(wHistoryTaskSelfList);

            //停线停序原因赋值
            for (SFCTaskSelf wSFCTaskSelf : wSFCTaskSelfList) {
                if (wEXCLineStopList.stream().anyMatch(p -> p.OrderID == wSFCTaskSelf.OrderID
                        && p.PartPointList.stream().anyMatch(q -> q.RoutePartPointID == wSFCTaskSelf.RoutePartPointID))) {
                    wSFCTaskSelf.EXCLineStop = wEXCLineStopList.stream().filter(p -> p.OrderID == wSFCTaskSelf.OrderID
                            && p.PartPointList.stream().anyMatch(q -> q.RoutePartPointID == wSFCTaskSelf.RoutePartPointID)).findFirst().get();
                    wSFCTaskSelf.StopReason = wSFCTaskSelf.EXCLineStop.Remark;
                }
            }
            //查询返修派工任务（忽略时间）
            wParams.clear();
            wParams.put("StationID", StringUtils.Join(",", wFMCIPConfig.StationIDList));
            wParams.put("ShiftType", wCurrentShift.ShiftType);
            wParams.put("Active", 1);
            wParams.put("WorkerID", wLoginUser.ID);
            wParams.put("Status", "1,2,3");
            wParams.put("IsRepairPlan", 1);
            List<SFCTaskSelf> wRepairTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            if (wRepairTaskSelfList != null && wRepairTaskSelfList.size() > 0) {
                wSFCTaskSelfList.addAll(wRepairTaskSelfList);
            }

            double wDayPlan = wSFCTaskSelfList.stream().mapToDouble(p -> p.PlantNum).sum();
            double wDayAct = wSFCTaskSelfList.stream().mapToDouble(p -> p.ActNum).sum();

            //四舍五入
            wDayPlan = new BigDecimal(wDayPlan).setScale(1, RoundingMode.HALF_UP).doubleValue();

            int wCurrentShiftType = wCurrentShift.ShiftType;
            List<SFCTaskSelf> wShiftTaskSelfList =
                    wSFCTaskSelfList.stream().filter(p -> p.ShiftType == wCurrentShiftType).collect(Collectors.toList());
            double wShiftPlan = wShiftTaskSelfList.stream().mapToDouble(p -> p.PlantNum).sum();
            double wShiftAct = wShiftTaskSelfList.stream().mapToDouble(p -> p.ActNum).sum();

            //四舍五入
            wShiftPlan = new BigDecimal(wShiftPlan).setScale(1, RoundingMode.HALF_UP).doubleValue();

            wResult.CustomResult.put("DayPlanFQTY", wDayPlan);
            wResult.CustomResult.put("DayActFQTY", wDayAct);
            wResult.CustomResult.put("ShiftPlanFQTY", wShiftPlan);
            wResult.CustomResult.put("ShiftActFQTY", wShiftAct);
            //⑥获取待生产任务
            List<SFCTaskSelf> wToProductList =
                    wShiftTaskSelfList.stream().filter(p -> p.Status != SFCTaskStatus.Done.getValue()).sorted(Comparator.comparing(SFCTaskSelf::getRoutePartOrder)
                            .thenComparing(SFCTaskSelf::getRoutePartPointOrder)).collect(Collectors.toList());
            //维护待生产任务的产出数量
            MaintenanceOutputQuantity(wLoginUser, wToProductList, wErrorCode);
            wResult.CustomResult.put("ToProductList", wToProductList);
            //⑦获取报工记录
            wParams.clear();
            wParams.put("StationID", StringUtils.Join(",", wFMCIPConfig.StationIDList));
            wParams.put("CStartTime", wSTime);
            wParams.put("CEndTime", wETime);
            List<SFCTaskReport> wSFCTaskReportList = mSFCTaskReportService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            wResult.CustomResult.put("SFCTaskReportList", wSFCTaskReportList);

            //⑧获取待交接报工单角标数
            wParams.clear();
            List<SFCTaskReport> wToConfirmList = SFCServiceImpl.getInstance().SFC_QueryToConfirmList(wLoginUser,
                    wParams, Pagination.getNewMaxSize()).Result;
            wResult.CustomResult.put("ToConfirmNumber", wToConfirmList.size());

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 翻译设备名称
     */
    private Map<Integer, List<DMSDeviceLedger>> AsignDeviceName(BMSEmployee wLoginUser,
                                                                List<FMCStation> wFMCStationList,
                                                                OutResult<Integer> wErrorCode) {
        Map<Integer, List<DMSDeviceLedger>> wResult = new HashMap<>();
        try {
            List<Integer> wDeviceIDList = new ArrayList<>();
            for (FMCStation wFMCStation : wFMCStationList) {
                if (wFMCStation.DeviceID == null || wFMCStation.DeviceID.size() == 0) {
                    continue;
                }
                for (int wDeviceID : wFMCStation.DeviceID) {
                    if (wDeviceIDList.stream().anyMatch(p -> p == wDeviceID)) {
                        continue;
                    }
                    wDeviceIDList.add(wDeviceID);
                }
            }
            if (wDeviceIDList.size() == 0) {
                return wResult;
            }
            Map<String, Object> wParams = new HashMap<String, Object>();
            List<DMSDeviceLedger> wDMSDeviceLedgerList = mDMSDeviceLedgerService.SelectPOList(wLoginUser, wDeviceIDList,
                    wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            Map<Integer, String> wIDNameMap =
                    wDMSDeviceLedgerList.stream().collect(Collectors.toMap(DMSDeviceLedger::getID
                            , DMSDeviceLedger::getName));
            Map<Integer, String> wIDCodeMap =
                    wDMSDeviceLedgerList.stream().collect(Collectors.toMap(DMSDeviceLedger::getID
                            , DMSDeviceLedger::getCode));
            for (FMCStation wFMCStation : wFMCStationList) {
                if (wFMCStation.DeviceID == null || wFMCStation.DeviceID.size() == 0) {
                    continue;
                }
                List<String> wNameList = new ArrayList<>();
                List<String> wCodeList = new ArrayList<>();
                for (int wDeviceID : wFMCStation.DeviceID) {
                    if (wIDNameMap.containsKey(wDeviceID)) {
                        wNameList.add(wIDNameMap.get(wDeviceID));
                    }
                    if (wIDCodeMap.containsKey(wDeviceID)) {
                        wCodeList.add(wIDCodeMap.get(wDeviceID));
                    }
                }
                wFMCStation.DeviceName = StringUtils.Join(",", wNameList);
                wFMCStation.DeviceCode = StringUtils.Join(",", wCodeList);

                List<DMSDeviceLedger> wList =
                        wDMSDeviceLedgerList.stream().filter(p -> wFMCStation.DeviceID.stream().anyMatch(q -> q == p.ID)).collect(Collectors.toList());
                if (wList.size() > 0) {
                    wResult.put(wFMCStation.ID, wList);
                }
            }
        } catch (Exception ex) {
            log.error("AsignDeviceName", ex);
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<SFCTaskSelf>> SFC_QueryResumptionList(BMSEmployee wLoginUser, String ipAddress,
                                                                    int wShiftType) {
        ServiceResult<List<SFCTaskSelf>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            //①获取当前工位配置
            wParams.put("IPAddress", ipAddress);
            wParams.put("Active", 1);
            FMCIPConfig wFMCIPConfig = mFMCIPConfigService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFMCIPConfig == null || wFMCIPConfig.ID <= 0 || wFMCIPConfig.StationIDList == null || wFMCIPConfig.StationIDList.size() <= 0) {
                wResult.FaultCode += StringUtils.Format("当前工控机（IP：{0}）未配置工位列表!", ipAddress);
                return wResult;
            }

            List<FMCShift> wFMCShiftList =
                    FMCServiceImpl.getInstance().FMC_QueryShiftList(wLoginUser, 9, 0, 0, 0,
                            Pagination.getNewMaxSize()).Result;
            if (wFMCShiftList == null || wFMCShiftList.size() <= 0) {
                wResult.FaultCode += "未查询到工厂班次设置，请联系系统管理员!";
                return wResult;
            }

            int wYear = Calendar.getInstance().get(Calendar.YEAR);
            int wMonth = Calendar.getInstance().get(Calendar.MONTH);
            int wDate = Calendar.getInstance().get(Calendar.DATE);

            FMCShift wCurrentShift = new FMCShift();
            if (wShiftType > 0) {
                if (wFMCShiftList.stream().noneMatch(p -> p.ShiftType == wShiftType)) {
                    wResult.FaultCode += "工厂班次设置中不存在所给班次，请联系系统管理员!";
                    return wResult;
                }
                wCurrentShift = wFMCShiftList.stream().filter(p -> p.ShiftType == wShiftType).findFirst().get();
            } else {
                for (FMCShift wFMCShift : wFMCShiftList) {
                    wFMCShift.StartTime.set(wYear, wMonth, wDate);
                    wFMCShift.EndTime.set(wYear, wMonth, wDate);
                    if (Calendar.getInstance().compareTo(wFMCShift.StartTime) >= 0 && Calendar.getInstance().compareTo(wFMCShift.EndTime) <= 0) {
                        wCurrentShift = wFMCShift;
                    }
                }
            }
            if (wCurrentShift.ID <= 0) {
                wResult.FaultCode += "未查询到当前班次信息，请检查班次设置!";
                return wResult;
            }

            //⑤获取当日计划、当日实际、当班计划、当班实际
            wParams.clear();
            wParams.put("StationID", StringUtils.Join(",", wFMCIPConfig.StationIDList));
            wParams.put("StaStartTime", CalendarTool.getDate());
            wParams.put("StaEndTime", CalendarTool.getDateMaxMill());
            wParams.put("ShiftType", wCurrentShift.ShiftType);
            wParams.put("WorkerID", wLoginUser.ID);
            wParams.put("Status", SFCTaskStatus.Done.getValue());
            wParams.put("IsRepairPlan", 0);
            wParams.put("Active", 1);
            wResult.Result = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);

            //查询返修的复工列表
            wParams.clear();
            wParams.put("StationID", StringUtils.Join(",", wFMCIPConfig.StationIDList));
            wParams.put("ShiftType", wCurrentShift.ShiftType);
            wParams.put("WorkerID", wLoginUser.ID);
            wParams.put("StaStartTime", CalendarTool.getDate());
            wParams.put("StaEndTime", CalendarTool.getDateMaxMill());
            wParams.put("Status", SFCTaskStatus.Done.getValue());
            wParams.put("IsRepairPlan", 1);
            wParams.put("Active", 1);
            List<SFCTaskSelf> wRepairList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            if (wRepairList != null && wRepairList.size() > 0) {
                wResult.Result.addAll(wRepairList);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_NumberReport(BMSEmployee wLoginUser, SFCTaskReport wSFCTaskReport) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            List<SFCTaskIPT> wTaskList;
            int wAffectRows;
            Map<String, Object> wParams = new HashMap<String, Object>();
            FPCRoutePartPoint wFPCRoutePartPoint;
            //查询工艺工序
            wParams.put("ID", wSFCTaskReport.RoutePartPointID);
            wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //判断该工序是否已停序
            if (EXCServiceImpl.getInstance().EXC_IsRoutePartPointStop(wLoginUser, wSFCTaskReport.OrderID,
                    wSFCTaskReport.RoutePartPointID).Result) {
                wResult.FaultCode += StringUtils.Format("操作失败：工序【{0}】已停序!!!", wFPCRoutePartPoint.Name);
                return wResult;
            }

            //③若是，则生成报工单
            wSFCTaskReport.setUserInfo(wLoginUser);
            if (wFPCRoutePartPoint.HandoverSemiProduct == 1) {
                wSFCTaskReport.Status = SFCTaskReportStatus.TO_CONFIRM.getValue();
                //维护待确认人
                wSFCTaskReport.HandoverList = getHandoverList(wLoginUser, wFPCRoutePartPoint, wSFCTaskReport.OrderID,
                        wErrorCode);
                if (wSFCTaskReport.HandoverList == null || wSFCTaskReport.HandoverList.size() <= 0) {
                    wResult.FaultCode += "未找到后工序交接人，请联系后工序班组长派工!";
                    return wResult;
                }
            } else {
                wSFCTaskReport.ConfirmID = BMSEmployee.SysAdmin.ID;
                wSFCTaskReport.Status = SFCTaskReportStatus.CONFIRMED.getValue();

                //库存扣减
                TaskTimerUtils.AddTask("StockDeduct",
                        () -> StockDeduct(wLoginUser, wSFCTaskReport, wErrorCode)
                );
            }
            wSFCTaskReport.ReportMode = SFCTaskReportMode.NUMBER_REPORT.getValue();
            wSFCTaskReport.RoutePartPointName = wFPCRoutePartPoint.Name;
            wAffectRows = mSFCTaskReportService.UpdatePo(wLoginUser, wSFCTaskReport, wErrorCode);
            if (wAffectRows <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            //本工序设置需要交接，则后续报工数量交接后维护
            if (wFPCRoutePartPoint.HandoverSemiProduct == 1) {
                return wResult;
            }
            //更新计划实时进度
            TaskTimerUtils.AddTask("UpdatesPlanRealTimeProgress",
                    () -> UpdatesPlanRealTimeProgress(wLoginUser,
                            new ArrayList<>(Collections.singletonList(wSFCTaskReport.TaskSelfID)),
                            new ArrayList<>(Collections.singletonList(wSFCTaskReport.TaskPartPointDayID)), wErrorCode)
            );

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_TrayBindingComponents(BMSEmployee wLoginUser, String wTrayCode,
                                                            String wComponentCodes,
                                                            List<SFCTaskSelf> wSFCTaskSelfList, String wMoldCode) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //托盘绑定模具，判断任务产出是否为成品，若不是，则报错
            if (StringUtils.isNotEmpty(wMoldCode)) {
                Optional<SFCTaskSelf> first =
                        wSFCTaskSelfList.stream().filter(p -> p.ComponentType != PDMComponentTypes.Product.getValue()).findFirst();
                if (first.isPresent()) {
                    SFCTaskSelf sfcTaskSelf = first.get();
                    wResult.FaultCode += StringUtils.Format("工单【{0}】选择错误，装模时工序产出类型必须配置为`成品`，请检查工艺配置!",
                            sfcTaskSelf.Code);
                    return wResult;
                }
            }

            //①验证托盘码是否正确
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("CodeEQ", wTrayCode);
            DMSFrockLedger wDMSFrockLedger = mDMSFrockLedgerService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wDMSFrockLedger == null || wDMSFrockLedger.ID <= 0 || wDMSFrockLedger.FrockType != AMSFrockType.TP.getValue()) {
                wResult.FaultCode += StringUtils.Format("托盘码【{0}】错误!", wTrayCode);
                return wResult;
            }
            //①验证模具是否正确
            DMSFrockLedger wModuleFrockLedger = new DMSFrockLedger();
            if (StringUtils.isNotEmpty(wMoldCode)) {
                wParams.clear();
                wParams.put("CodeEQ", wMoldCode);
                wModuleFrockLedger = mDMSFrockLedgerService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wModuleFrockLedger == null || wModuleFrockLedger.ID <= 0 || wModuleFrockLedger.FrockType != AMSFrockType.MJ.getValue()) {
                    wResult.FaultCode += StringUtils.Format("模具码【{0}】错误!", wMoldCode);
                    return wResult;
                }
            }

            //①验证托盘是否处于未绑定的状态
            String wFaultCode = CheckTrayCodeStatus(wLoginUser, wTrayCode);
            if (StringUtils.isNotEmpty(wFaultCode)) {
                wResult.FaultCode += wFaultCode;
                return wResult;
            }
            //②验证工件记录是否存在
            List<SFCWorkpieceRecord> wSFCWorkpieceRecordList = GetSFCWorkpieceRecordByComponentCodes(wLoginUser,
                    wComponentCodes);
            for (String wComponetCode : wComponentCodes.split(",")) {
                if (wSFCWorkpieceRecordList.stream().noneMatch(p -> p.Code.equals(wComponetCode))) {
                    wResult.FaultCode += StringUtils.Format("未查询到【{0}】的工件记录!", wComponetCode);
                    return wResult;
                }

                SFCWorkpieceRecord sfcWorkpieceRecord =
                        wSFCWorkpieceRecordList.stream().filter(p -> p.Code.equals(wComponetCode)).findFirst().get();
                if (sfcWorkpieceRecord.CheckResult != 1) {
                    wResult.FaultCode += StringUtils.Format("工件【{0}】的检验结果不合格!", wComponetCode);
                    return wResult;
                }
            }
            //③验证模具是否已绑定
            if (StringUtils.isNotEmpty(wMoldCode)) {
                wParams.clear();
                wParams.put("ComponentCode", wMoldCode);
                wParams.put("ComponentType", PDMComponentTypes.Mold.getValue());
                wParams.put("Status", SFCFrockBindRecordStatus.BINDING.getValue());
                List<SFCFrockBindRecord> sfcFrockBindRecords = mSFCFrockBindRecordService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(),
                        wErrorCode);
                if (sfcFrockBindRecords != null && sfcFrockBindRecords.size() > 0) {
                    wResult.FaultCode += StringUtils.Format("模具【{0}】已绑定!", wMoldCode);
                    return wResult;
                }
            }

            //③验证不同订单的工件的工艺参数是否相同
            wFaultCode = CheckComponentCodes(wLoginUser, wSFCTaskSelfList, wComponentCodes, wSFCWorkpieceRecordList);
            if (StringUtils.isNotEmpty(wFaultCode)) {
                wResult.FaultCode += wFaultCode;
                return wResult;
            }
            //④新增绑定工件记录
            List<SFCToolUsageRecord> wSFCToolUsageRecordList = new ArrayList<>();
            SFCToolUsageRecord wSFCToolUsageRecord;
            String[] wComponentCodeList = wComponentCodes.split(",");
            for (String wComponentCode : wComponentCodeList) {

                SFCFrockBindRecord wSFCFrockBindRecord = new SFCFrockBindRecord();
                wSFCFrockBindRecord.setUserInfo(wLoginUser);
                wSFCFrockBindRecord.ComponentCode = wComponentCode;
                wSFCFrockBindRecord.Status = SFCFrockBindRecordStatus.BINDING.getValue();
                wSFCFrockBindRecord.ComponentType = PDMComponentTypes.Component.getValue();

                Optional<SFCWorkpieceRecord> first =
                        wSFCWorkpieceRecordList.stream().filter(p -> p.Code.equals(wComponentCode)).findFirst();
                first.ifPresent(sfcWorkpieceRecord -> wSFCFrockBindRecord.ComponentType =
                        sfcWorkpieceRecord.ComponentType);

                wSFCFrockBindRecord.FrockLedgerID = wDMSFrockLedger.ID;
                wSFCFrockBindRecord.FrockLedgerCode = wDMSFrockLedger.Code;
                wSFCFrockBindRecord.Active = 1;
                //⑤保存
                mSFCFrockBindRecordService.UpdatePo(wLoginUser, wSFCFrockBindRecord, wErrorCode);

                //增加工装使用记录
                wSFCToolUsageRecord = new SFCToolUsageRecord(first.get().ID, wDMSFrockLedger.ID,
                        PDMComponentTypes.Tray.getValue());
                wSFCToolUsageRecord.setUserInfo(wLoginUser);
                wSFCToolUsageRecordList.add(wSFCToolUsageRecord);
                if (StringUtils.isNotEmpty(wMoldCode)) {
                    wSFCToolUsageRecord = new SFCToolUsageRecord(first.get().ID, wModuleFrockLedger.ID,
                            PDMComponentTypes.Mold.getValue());
                    wSFCToolUsageRecord.setUserInfo(wLoginUser);
                    wSFCToolUsageRecordList.add(wSFCToolUsageRecord);
                }
            }
            //保存工装使用记录
            mSFCToolUsageRecordService.UpdatePoList(wLoginUser, wSFCToolUsageRecordList, wErrorCode);
            //⑤芯子绑定模具记录
            if (StringUtils.isNotEmpty(wMoldCode)) {
                SFCFrockBindRecord wSFCFrockBindRecord = new SFCFrockBindRecord();
                wSFCFrockBindRecord.setUserInfo(wLoginUser);
                wSFCFrockBindRecord.ComponentCode = wMoldCode;
                wSFCFrockBindRecord.Status = SFCFrockBindRecordStatus.BINDING.getValue();
                wSFCFrockBindRecord.ComponentType = PDMComponentTypes.Mold.getValue();
                wSFCFrockBindRecord.FrockLedgerID = wDMSFrockLedger.ID;
                wSFCFrockBindRecord.FrockLedgerCode = wDMSFrockLedger.Code;
                wSFCFrockBindRecord.Active = 1;
                mSFCFrockBindRecordService.UpdatePo(wLoginUser, wSFCFrockBindRecord, wErrorCode);
            }

            //`装模`后，自动创建成品码并与芯子进行组装
            if (wSFCTaskSelfList.get(0).ComponentType == PDMComponentTypes.Product.getValue()) {
                String faultCode = SFCFrockBindServiceImpl.getInstance().SFC_AutoAssembly(wLoginUser,
                        wSFCTaskSelfList.get(0), wTrayCode, wErrorCode).FaultCode;
                if (StringUtils.isNotEmpty(faultCode)) {
                    wResult.FaultCode += faultCode;
                    return wResult;
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_TrayBindingComponents", e);
        }
        return wResult;
    }

    /**
     * 验证工艺参数是否相同
     */
    private String CheckComponentCodes(BMSEmployee wLoginUser, List<SFCTaskSelf> wSFCTaskSelfList,
                                       String wComponentCodes, List<SFCWorkpieceRecord> wSFCWorkpieceRecordList) {
        String wResult = "";
        try {
            OutResult<Integer> wErrorCode = new OutResult<Integer>(0);

            List<Integer> wRoutePartPointIDList =
                    wSFCTaskSelfList.stream().map(p -> p.RoutePartPointID).distinct().collect(Collectors.toList());
            String wRoutePartPointIDs = StringUtils.Join(",", wRoutePartPointIDList);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("RoutePartPointID", wRoutePartPointIDs);
            List<FPCRouteStep> wFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            for (int wRoutePartPointID : wRoutePartPointIDList) {
                List<FPCRouteStep> wList =
                        wFPCRouteStepList.stream().filter(p -> p.RoutePartPointID == wRoutePartPointID).collect(Collectors.toList());
                for (FPCRouteStep wFPCRouteStep : wList) {
                    if (wFPCRouteStepList.stream().anyMatch(p -> p.RoutePartPointID != wRoutePartPointID
                            && p.CollectionNumber.equals(wFPCRouteStep.CollectionNumber)
                            && !p.StandardValue.equals(wFPCRouteStep.StandardValue))) {
                        SFCTaskSelf sfcTaskSelf =
                                wSFCTaskSelfList.stream().filter(p -> p.RoutePartPointID == wFPCRouteStep.RoutePartPointID).findFirst().get();
                        SFCWorkpieceRecord sfcWorkpieceRecord =
                                wSFCWorkpieceRecordList.stream().filter(p -> p.OrderID == sfcTaskSelf.OrderID).findFirst().get();

                        wResult = StringUtils.Format("工件【{0}】的工艺参数【{1}】与其他工件不相同!", sfcWorkpieceRecord.Code,
                                wFPCRouteStep.CollectionNumber);
                        return wResult;
                    }
                }
            }
        } catch (Exception ex) {
            log.error("CheckComponentCodes", ex);
        }
        return wResult;
    }

    /**
     * 根据工件码获取工件记录
     */
    private List<SFCWorkpieceRecord> GetSFCWorkpieceRecordByComponentCodes(BMSEmployee wLoginUser,
                                                                           String wComponentCodes) {
        List<SFCWorkpieceRecord> wResult = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            if (StringUtils.isEmpty(wComponentCodes)) {
                return wResult;
            }

            Map<String, Object> wParams = new HashMap<String, Object>();


            String[] wCodeList = wComponentCodes.split(",");
            for (String wCode : wCodeList) {
                wParams.clear();
                wParams.put("CodeEQ", wCode);

                SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wSFCWorkpieceRecord.ID > 0) {
                    wResult.add(wSFCWorkpieceRecord);
                }
            }
        } catch (Exception e) {
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 检查托盘是否处于未绑定的状态
     */
    private String CheckTrayCodeStatus(BMSEmployee wLoginUser, String wTrayCode) {
        String wResult = "";
        try {
            OutResult<Integer> wErrorCode = new OutResult<Integer>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("FrockLedgerCode", wTrayCode);
            wParams.put("Status", SFCFrockBindRecordStatus.BINDING.getValue());
            List<SFCFrockBindRecord> wSFCFrockBindRecordList = mSFCFrockBindRecordService.SelectPOList(wLoginUser,
                    wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCFrockBindRecordList.size() > 0) {
                wResult = StringUtils.Format("托盘【{0}】绑定失败，该托盘已被使用，需要解绑后才能再次使用!", wTrayCode);
            }
        } catch (Exception e) {
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<SFCTaskIPT> SFC_CreateTaskByWorkpieceNoAndTask(BMSEmployee wLoginUser, int wStationID,
                                                                        int wCheckType, String wWorkpieceNo,
                                                                        SFCTaskSelf wSFCTaskSelf) {
        ServiceResult<SFCTaskIPT> wResult = new ServiceResult<>();
        wResult.Result = new SFCTaskIPT();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();

            //判断该工序是否已停序
            if (EXCServiceImpl.getInstance().EXC_IsRoutePartPointStop(wLoginUser, wSFCTaskSelf.OrderID,
                    wSFCTaskSelf.RoutePartPointID).Result) {
                wResult.FaultCode += StringUtils.Format("操作失败：工序【{0}】已停序!!!", wSFCTaskSelf.RoutePartPointName);
                return wResult;
            }

            //判断是否为返修计划指定的返修工件
            if (wSFCTaskSelf.IsRepairPlan == 1 && StringUtils.isNotEmpty(wWorkpieceNo)) {
                wParams.put("OrderID", wSFCTaskSelf.OrderID);
                wParams.put("WorkpieceNoList", wWorkpieceNo);
                List<EXCProductRepair> excProductRepairs = mEXCProductRepairService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                int wRoutePartPointID = wSFCTaskSelf.RoutePartPointID;
                if (excProductRepairs == null || excProductRepairs.size() <= 0 ||
                        excProductRepairs.stream().noneMatch(p -> p.PartPointList != null
                                && p.PartPointList.size() > 0 && p.PartPointList.stream().anyMatch(q -> q.RoutePartPointID == wRoutePartPointID))) {
                    wResult.FaultCode += StringUtils.Format("操作失败：工件码【{0}】错误，请输入正确的返修工件码!!!", wWorkpieceNo);
                    return wResult;
                }
            }

            //验证工位工序实时物料是否满足
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            wParams.put("IsCodeScanning", 1);
            List<MSSBOMItem> wMSSBOMItemList = mMSSBOMItemService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            if (wMSSBOMItemList != null && wMSSBOMItemList.size() > 0) {
                wParams.clear();
                wParams.put("StationID", wStationID);
                wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                List<SFCStationMaterial> wSFCStationMaterialList = mSFCStationMaterialService.SelectPOList(wLoginUser,
                        wParams, Pagination.getNewMaxSize(), wErrorCode);
                if (wMSSBOMItemList.stream().anyMatch(p -> wSFCStationMaterialList.stream().noneMatch(q -> q.MaterialID == p.MaterialID))) {
                    MSSBOMItem wMSSBOMItem =
                            wMSSBOMItemList.stream().filter(p -> wSFCStationMaterialList.stream().noneMatch(q -> q.MaterialID == p.MaterialID)).findFirst().get();
                    wResult.FaultCode += StringUtils.Format("未查询到【{0}-{1}】物料批次，请及时上料!",
                            wMSSBOMItem.MaterialNo, wMSSBOMItem.MaterialName);
                    return wResult;
                }
            }

            //验证该工件的前工序是否已做自检任务，若没有，则报错提示
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.RoutePartPointID);
            FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            if (wFPCRoutePartPoint.PrevPartPointID > 0) {
                //前工艺工序
                wParams.clear();
                wParams.put("ID", wFPCRoutePartPoint.PrevPartPointID);
                FPCRoutePartPoint wPrevRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wPrevRoutePartPoint == null || wPrevRoutePartPoint.ID <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }

                //前工序为数量报工，仅检查工件是否录入过系统
                if (wPrevRoutePartPoint.ReportMode == FPCTaskReportMode.NUMBER_REPORT.getValue()
                        && wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType) {
                    wParams.clear();
                    wParams.put("CodeEQ", wWorkpieceNo);
                    SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                        wResult.FaultCode += StringUtils.Format("工件【{0}】未录入系统!", wWorkpieceNo);
                        return wResult;
                    }
                } else {
                    //前工序自检单
                    wParams.clear();
                    wParams.put("OrderID", wSFCTaskSelf.OrderID);
                    wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                    wParams.put("ComponentCode", wWorkpieceNo);
                    wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                    wParams.put("Status", SFCTaskStatus.Done.getValue());
                    SFCTaskIPT wPrevSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
                    if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                            && (wPrevSFCTaskIPT == null || wPrevSFCTaskIPT.ID <= 0)) {
                        wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的自检!", wWorkpieceNo,
                                wFPCRoutePartPoint.PrevPartPointName);
                        return wResult;
                    }
                    //验证该工件的前工序是否报工
                    if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                            && wPrevSFCTaskIPT.IsReported != 1) {
                        wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的报工或报工确认!", wWorkpieceNo,
                                wFPCRoutePartPoint.PrevPartPointName);
                        return wResult;
                    }
                    //验证该工件的前工序是否设置的强制做专检，且该工件在前工序已做专检，否则报错提示
                    if (wPrevRoutePartPoint.IsSpecialCheck == 1) {
                        wParams.clear();
                        wParams.put("OrderID", wSFCTaskSelf.OrderID);
                        wParams.put("RoutePartPointID", wFPCRoutePartPoint.PrevPartPointID);
                        wParams.put("ComponentCode", wWorkpieceNo);
                        wParams.put("Type", FPCCheckType.SPECIAL_CHECK.getValue());
                        wParams.put("Status", SFCTaskStatus.Done.getValue());
                        SFCTaskIPT wPrevSpecialTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams, wErrorCode);
                        if (wFPCRoutePartPoint.ComponentType == wPrevRoutePartPoint.ComponentType
                                && (wPrevSpecialTaskIPT == null || wPrevSpecialTaskIPT.ID <= 0)) {
                            wResult.FaultCode += StringUtils.Format("工件【{0}】未做【{1}】工序的专检!", wWorkpieceNo,
                                    wFPCRoutePartPoint.PrevPartPointName);
                            return wResult;
                        }
                    }
                }

                //组装工序时，验证工件是否已绑定子工件
                if (wFPCRoutePartPoint.ReportMode != SFCTaskReportMode.NUMBER_REPORT.getValue()
                        && wPrevRoutePartPoint.ComponentType != wFPCRoutePartPoint.ComponentType) {

                    //判断是否取消组装检查
                    wParams.clear();
                    wParams.put("ID", wSFCTaskSelf.StationID);
                    FMCStation fmcStation = mFMCStationService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (fmcStation.IsCancelAssemblyCheck != 1) {
                        wParams.clear();
                        wParams.put("TargetCode", wWorkpieceNo);
                        wParams.put("TargetComponentType", wSFCTaskSelf.ComponentType);
                        List<SFCAssemblyRecord> wSFCAssemblyRecordList =
                                mSFCAssemblyRecordService.SelectPOList(wLoginUser,
                                        wParams, Pagination.getNewMaxSize(),
                                        wErrorCode);
                        if (wSFCAssemblyRecordList == null || wSFCAssemblyRecordList.size() <= 0) {
                            wResult.FaultCode += StringUtils.Format("{2}【{0}】未与其他{1}绑定,请先组装!", wWorkpieceNo,
                                    PDMComponentTypes.getEnumType(wPrevRoutePartPoint.ComponentType).getLabel(),
                                    PDMComponentTypes.getEnumType(wFPCRoutePartPoint.ComponentType).getLabel());
                            return wResult;
                        }
                    }
                }
            }

            wParams.clear();
            wParams.put("CodeEQ", wWorkpieceNo);
            SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            //③若没有，则创建工件流转记录；若有，则判断是否已绑定至其他订单，若是，则报错提示
            Calendar wBaseTime = Calendar.getInstance();
            wBaseTime.set(2000, 0, 1, 0, 0, 0);
            int wAffectRows;
            if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID == 0) {
                wSFCWorkpieceRecord = new SFCWorkpieceRecord();
                wSFCWorkpieceRecord.setUserInfo(wLoginUser);
                wSFCWorkpieceRecord.Code = wWorkpieceNo;
                wSFCWorkpieceRecord.ID = 0;
                wSFCWorkpieceRecord.OffLineStationID = 0;
                wSFCWorkpieceRecord.OffLineTime = wBaseTime;
                wSFCWorkpieceRecord.OnLineStationID = wSFCTaskSelf.StationID;
                wSFCWorkpieceRecord.OnLineTime = Calendar.getInstance();
                wSFCWorkpieceRecord.OrderID = wSFCTaskSelf.OrderID;
                wSFCWorkpieceRecord.OrderNo = wSFCTaskSelf.OrderNo;
                wSFCWorkpieceRecord.ProductID = wSFCTaskSelf.ProductID;
                wSFCWorkpieceRecord.ComponentType = wSFCTaskSelf.ComponentType;
                if (wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.ToCheck.getValue()) {
                    wSFCWorkpieceRecord.FirstFlag = 1;
                }
                wSFCWorkpieceRecord.Status = SFCTaskStatus.Todo.getValue();
                wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }
            } else {
                //判断工件状态，若为`报废`，不允许流转
                if (wSFCWorkpieceRecord.Status == SFCTaskStatus.Scarp.getValue()) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工件已报废!!!", wWorkpieceNo);
                    return wResult;
                }

                if (wSFCWorkpieceRecord.OrderID != wSFCTaskSelf.OrderID) {
                    wResult.FaultCode += StringUtils.Format("【{0}】工件已与订单【{1}】绑定!", wWorkpieceNo,
                            wSFCWorkpieceRecord.OrderNo);
                    return wResult;
                }
            }

            //维护工件的履历（上线信息）
            SFCWorkpieceResume wSFCWorkpieceResume = new SFCWorkpieceResume(wSFCWorkpieceRecord.ID,
                    wSFCTaskSelf.StationID, wSFCTaskSelf.RoutePartPointID, Calendar.getInstance(), wLoginUser.ID, 0);
            wSFCWorkpieceResume.setUserInfo(wLoginUser);
            wAffectRows = mSFCWorkpieceResumeService.UpdatePo(wLoginUser, wSFCWorkpieceResume, wErrorCode);
            if (wAffectRows <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //④根据工件号查询检验单，若存在，则报错提示；若不存在则创建检验单（自检、互检单）
            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Type", wCheckType);
            wParams.put("ComponentCode", wWorkpieceNo);
            wParams.put("ProductRepairID", wSFCTaskSelf.ProductRepairID);
            SFCTaskIPT wSFCTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskIPT.ID > 0) {
                wResult.FaultCode += StringUtils.Format("【{0}】该工件已创建当前工序的{1}单!", wWorkpieceNo,
                        FPCCheckType.getEnumType(wCheckType).getLabel());
                wResult.Result = wSFCTaskIPT;
                return wResult;
            } else {
                //根据日计划ID查询派工记录
                wParams.clear();
                wParams.put("TaskPartPointDayID", wSFCTaskSelf.TaskPartPointDayID);
                List<SFCTaskSelf> wSFCTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(), wErrorCode);

                wSFCTaskIPT = new SFCTaskIPT();
                wSFCTaskIPT.setUserInfo(wLoginUser);
                wSFCTaskIPT.OrderID = wSFCTaskSelf.OrderID;
                wSFCTaskIPT.OrderNo = wSFCTaskSelf.OrderNo;
                wSFCTaskIPT.RoutePartID = wSFCTaskSelf.RoutePartID;
                wSFCTaskIPT.RoutePartPointID = wSFCTaskSelf.RoutePartPointID;
                wSFCTaskIPT.ProductID = wSFCTaskSelf.ProductID;
                wSFCTaskIPT.TaskPartID = wSFCTaskSelf.TaskPartID;
                wSFCTaskIPT.TaskPartPointID = wSFCTaskSelf.TaskPartPointID;
                wSFCTaskIPT.TaskPartPointDayID = wSFCTaskSelf.TaskPartPointDayID;
                wSFCTaskIPT.TaskSelfID = wSFCTaskSelf.ID;
                wSFCTaskIPT.WorkShopID = wSFCTaskSelf.WorkShopID;
                wSFCTaskIPT.WorkAreaID = wSFCTaskSelf.WorkAreaID;
                wSFCTaskIPT.StationID = wSFCTaskSelf.StationID;
                wSFCTaskIPT.ShiftType = wSFCTaskSelf.ShiftType;
                wSFCTaskIPT.StationDate = wSFCTaskSelf.StationDate;
                wSFCTaskIPT.Result = 1;
                if (wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.ToCheck.getValue()
                        || wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.Locked.getValue()) {
                    wSFCTaskIPT.FirstFlag = 1;
                }
                wSFCTaskIPT.WorkerIDList = new ArrayList<>();
                if (wSFCTaskSelfList.size() > 0) {
                    wSFCTaskIPT.WorkerIDList =
                            wSFCTaskSelfList.stream().map(p -> p.WorkerID).distinct().collect(Collectors.toList());
                }
                wSFCTaskIPT.Status = SFCTaskStatus.Default.getValue();
                wSFCTaskIPT.Type = wCheckType;
                wSFCTaskIPT.ComponentCode = wWorkpieceNo;
                wSFCTaskIPT.IsRepairPlan = wSFCTaskSelf.IsRepairPlan;
                wSFCTaskIPT.ProductRepairID = wSFCTaskSelf.ProductRepairID;
                wAffectRows = mSFCTaskIPTService.UpdatePo(wLoginUser, wSFCTaskIPT, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    wResult.Result = wSFCTaskIPT;
                    return wResult;
                }
            }
            //⑤返回前端
            wResult.Result = wSFCTaskIPT;

            //判断返修任务，若为返修任务，则返回历史检验值，并替换TaskID
            if (wSFCTaskSelf.IsRepairPlan == 1 && wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                //①根据返修单ID查询返修记录
                wParams.clear();
                wParams.put("ComponentCode", wWorkpieceNo);
                wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                wParams.put("OrderID", wSFCTaskSelf.OrderID);
                wParams.put("IsRepairPlan", 0);
                SFCTaskIPT sfcTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                //②根据检验单ID查询检验值
                if (sfcTaskIPT != null && sfcTaskIPT.ID > 0) {
                    wParams.clear();
                    wParams.put("TaskID", sfcTaskIPT.ID);
                    List<IPTValue> iptValues = mIPTValueService.SelectPOList(wLoginUser, wParams,
                            Pagination.getNewMaxSize(),
                            wErrorCode);
                    wResult.CustomResult.put("RepairValueList", iptValues);
                }
            }

            //自检作业时，若为返修任务，则创建返修记录
            if (wSFCTaskSelf.IsRepairPlan == 1 && wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                AddEXCProductRepairRecordItem(wLoginUser, wSFCTaskSelf, wSFCTaskIPT, wSFCWorkpieceRecord, wErrorCode);
            }

            //自检作业，若不为返修，则维护日计划的首检状态（是否需要首检）
            if (wSFCTaskSelf.IsRepairPlan != 1 && wCheckType == FPCCheckType.SELF_CHECK.getValue()
                    && wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.ToCheck.getValue()) {
                SFCFrockBindServiceImpl.getInstance().SFC_UpdateDayPlanFirstCheckStatus(wLoginUser, wSFCTaskSelf,
                        wFPCRoutePartPoint);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_TrayUnBindComponents(BMSEmployee wLoginUser, String wTrayCode) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①判断托盘是否处于绑定状态
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("FrockLedgerCode", wTrayCode);
            wParams.put("Status", SFCFrockBindRecordStatus.BINDING.getValue());
            List<SFCFrockBindRecord> wSFCFrockBindRecordList = mSFCFrockBindRecordService.SelectPOList(wLoginUser,
                    wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCFrockBindRecordList == null || wSFCFrockBindRecordList.size() <= 0) {
                wResult.FaultCode += StringUtils.Format("托盘【{0}】未处于绑定状态!", wTrayCode);
                return wResult;
            }
            //②解除绑定
            int wAffectRows = 0;
            for (SFCFrockBindRecord wSFCFrockBindRecord : wSFCFrockBindRecordList) {
                wSFCFrockBindRecord.setUserInfo(wLoginUser);
                wSFCFrockBindRecord.Status = SFCFrockBindRecordStatus.UNBINDING.getValue();
                wAffectRows = mSFCFrockBindRecordService.UpdatePo(wLoginUser, wSFCFrockBindRecord, wErrorCode);
                if (wAffectRows <= 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getValue();
                    return wResult;
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<FPCRouteStep>> SFC_QueryItemsByStationAndTask(BMSEmployee wLoginUser, int wStationID,
                                                                            int wCheckType, SFCTaskSelf wSFCTaskSelf,
                                                                            boolean wIsSpotCheck) {
        ServiceResult<List<FPCRouteStep>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();

            wParams.put("ID", wSFCTaskSelf.ID);
            wParams.put("Active", 1);
            wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.Logic.getLabel();
                return wResult;
            }

            wResult.CustomResult.put("SFCTaskSelf", wSFCTaskSelf);
            //②根据派工任务查询工步列表（根据检验类型）
            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            if (wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                wParams.put("IsSelfCheck", 1);
            } else if (wCheckType == FPCCheckType.MUTUAL_CHECK.getValue()) {
                wParams.put("IsMutualCheck", 1);
            } else if (wCheckType == FPCCheckType.SPECIAL_CHECK.getValue()) {
                wParams.put("IsSpecialCheck", 1);
            }
            List<FPCRouteStep> wFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            //④查询首检项
            if (wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                if (wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.ToCheck.getValue()
                        || wSFCTaskSelf.FirstCheckStatus == APSFirstCheckStatus.Locked.getValue() || wIsSpotCheck) {
                    wParams.clear();
                    wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                    wParams.put("Active", 1);
                    wParams.put("IsFirstCheck", 1);
                    List<FPCRouteStep> wFirstItemList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                            Pagination.getNewMaxSize(), wErrorCode);
                    for (FPCRouteStep wFPCRouteStep : wFirstItemList) {
                        if (wFPCRouteStepList.stream().noneMatch(p -> p.ID == wFPCRouteStep.ID)) {
                            wFPCRouteStepList.add(wFPCRouteStep);
                        }
                    }
                }
            }
            //③查询上工艺工序
            List<FPCRouteStep> wPrevRouteStepList = new ArrayList<>();
            if (wCheckType == FPCCheckType.SELF_CHECK.getValue()) {
                wParams.clear();
                wParams.put("ID", wSFCTaskSelf.RoutePartPointID);
                FPCRoutePartPoint wThisRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wThisRoutePartPoint != null && wThisRoutePartPoint.ID > 0 && wThisRoutePartPoint.PrevPartPointID > 0) {
                    wParams.clear();
                    wParams.put("ID", wThisRoutePartPoint.PrevPartPointID);
                    FPCRoutePartPoint wPrevRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (wPrevRoutePartPoint != null && wPrevRoutePartPoint.ID > 0) {
                        //④若上工艺工序的互检模式为本工序互检，则不查询上工序的互检工步
                        //⑤若上工艺工序的互检模式为下工序互检，则查询上工序的互检工步
                        if (wPrevRoutePartPoint.MutualModel == 1) {
                            wParams.clear();
                            wParams.put("RoutePartPointID", wPrevRoutePartPoint.ID);
                            wParams.put("Active", 1);
                            wParams.put("IsMutualCheck", 1);
                            wPrevRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                                    Pagination.getNewMaxSize(), wErrorCode);
                        }
                    }
                }
            }

            // 排序、返回结果
            wPrevRouteStepList.sort(Comparator.comparing(FPCRouteStep::getStepOrder));
            wFPCRouteStepList.sort(Comparator.comparing(FPCRouteStep::getStepOrder));
            wResult.Result.addAll(wPrevRouteStepList);
            wResult.Result.addAll(wFPCRouteStepList);
            //根据派工任务、检验类型查询检验单
            wParams.clear();
            wParams.put("OrderID", wSFCTaskSelf.OrderID);
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Type", wCheckType);
            wParams.put("Status", SFCTaskStatus.Default.getValue());
            List<SFCTaskIPT> wSFCTaskIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(), wErrorCode);
            int wTaskSelfID = wSFCTaskSelf.ID;
            if (wSFCTaskIPTList.size() > 0) {
                wSFCTaskIPTList.forEach(p -> p.TaskSelfID = wTaskSelfID);
                mSFCTaskIPTService.UpdatePoList(wLoginUser, wSFCTaskIPTList, wErrorCode);
            }
            //根据检验单查询检验值
            List<IPTValue> wValueList = new ArrayList<>();
            if (wSFCTaskIPTList.size() > 0) {
                wParams.clear();
                wParams.put("TaskID", StringUtils.Join(",",
                        wSFCTaskIPTList.stream().map(p -> p.ID).distinct().collect(Collectors.toList())));
                wValueList = mIPTValueService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(), wErrorCode);
            }
            //返回前端
            wResult.CustomResult.put("SFCTaskIPTList", wSFCTaskIPTList);
            wResult.CustomResult.put("ValueList", wValueList);

            //查询返修历史记录
            List<SFCTaskIPT> wRepairList =
                    wSFCTaskIPTList.stream().filter(p -> p.IsRepairPlan == 1).collect(Collectors.toList());
            List<IPTValue> wRepairValueList = new ArrayList<>();
            if (wRepairList.size() > 0) {
                //①根据返修单ID查询返修记录
                wParams.clear();
                wParams.put("ComponentCode", StringUtils.Join(",",
                        wRepairList.stream().map(p -> p.ComponentCode).collect(Collectors.toList())));
                wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                wParams.put("OrderID", wSFCTaskSelf.OrderID);
                wParams.put("IsRepairPlan", 0);
                List<SFCTaskIPT> sfcTaskIPTS = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                        wErrorCode);
                //②根据检验单ID查询检验值
                if (sfcTaskIPTS != null && sfcTaskIPTS.size() > 0) {
                    wParams.clear();
                    wParams.put("TaskID", StringUtils.Join(",",
                            sfcTaskIPTS.stream().map(p -> p.ID).collect(Collectors.toList())));
                    List<IPTValue> iptValues = mIPTValueService.SelectPOList(wLoginUser, wParams,
                            Pagination.getNewMaxSize(),
                            wErrorCode);
                    for (SFCTaskIPT sfcTaskIPT : wRepairList) {
                        Optional<SFCTaskIPT> first =
                                sfcTaskIPTS.stream().filter(p -> p.ComponentCode.equals(sfcTaskIPT.ComponentCode)).findFirst();
                        if (first.isPresent()) {
                            SFCTaskIPT sfcTaskIPT1 = first.get();
                            List<IPTValue> wTempValueList =
                                    iptValues.stream().filter(p -> p.TaskID == sfcTaskIPT1.ID).collect(Collectors.toList());
                            for (IPTValue iptValue : wTempValueList) {
                                iptValue.ID = 0;
                                iptValue.TaskID = sfcTaskIPT.ID;
                            }
                            wRepairValueList.addAll(wTempValueList);
                        }
                    }
                }
            }
            wResult.CustomResult.put("RepairValueList", wRepairValueList);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_WorkpieceAssembly(BMSEmployee wLoginUser, String wSourceCodes,
                                                        String wTargetCode, SFCTaskSelf wSFCTaskSelf,
                                                        int wAssemblyType) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //判断是否为设备调用，若是，则先查询工单
            if (StringUtils.isEmpty(wSFCTaskSelf.Code)) {
                Map<String, Object> wParams = new HashMap<String, Object>();
                wParams.put("ID", wSFCTaskSelf.ID);
                wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
            }

            List<String> wSourceCodeList = StringUtils.parseList(wSourceCodes.split(","));

            //①验证目标工件是否已绑定
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("TargetCode", wTargetCode);
            List<SFCAssemblyRecord> wSFCAssemblyRecordList = mSFCAssemblyRecordService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCAssemblyRecordList != null && wSFCAssemblyRecordList.size() > 0) {
                wResult.FaultCode += StringUtils.Format("目标条码【{0}】已绑定!", wTargetCode);
                return wResult;
            }

            //出厂条码的绑定逻辑
            if (wAssemblyType == 1) {
                String wMsg = FactoryBarcodeBinding(wLoginUser, wSourceCodes, wTargetCode, wErrorCode);
                if (StringUtils.isNotEmpty(wMsg)) {
                    wResult.FaultCode = wMsg;
                }
                return wResult;
            }

            //③查询产出类型
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.RoutePartPointID);
            FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //查询上道工序的产出类型
            if (wFPCRoutePartPoint.PrevPartPointID <= 0) {
                wResult.FaultCode += StringUtils.Format("工序【{0}】无前工序，无法组装!", wSFCTaskSelf.RoutePartPointName);
                return wResult;
            }

            wParams.clear();
            wParams.put("ID", wFPCRoutePartPoint.PrevPartPointID);
            FPCRoutePartPoint wPrevRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wPrevRoutePartPoint == null || wPrevRoutePartPoint.ID <= 0) {
                wResult.FaultCode += StringUtils.Format("工序【{0}】的前工序数据缺失，无法组装!", wSFCTaskSelf.RoutePartPointName);
                return wResult;
            }

            if (wPrevRoutePartPoint.ComponentType == wFPCRoutePartPoint.ComponentType) {
                wResult.FaultCode += StringUtils.Format("工序【{0}】与前工序产出类型一致，无法组装!", wSFCTaskSelf.RoutePartPointName);
                return wResult;
            }
            //查询产品组成，验证数量是否合法
            wParams.clear();
            wParams.put("ProductID", wSFCTaskSelf.ProductID);
            wParams.put("ComponentType", wPrevRoutePartPoint.ComponentType);
            wParams.put("TargetComponentType", wFPCRoutePartPoint.ComponentType);
            FPCProductCom fpcProductCom = mFPCProductComService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (fpcProductCom == null || fpcProductCom.ID <= 0) {
                wResult.FaultCode += StringUtils.Format("未查询到【{0}→{1}】的产品组成信息，无法组装!",
                        PDMComponentTypes.getEnumType(wPrevRoutePartPoint.ComponentType).getLabel(),
                        PDMComponentTypes.getEnumType(wFPCRoutePartPoint.ComponentType).getLabel());
                return wResult;
            }

            if (wSourceCodeList.size() != (int) fpcProductCom.ComponentNumber) {
                wResult.FaultCode += StringUtils.Format("组装成{1}需要的{0}数量为{2}!",
                        PDMComponentTypes.getEnumType(wPrevRoutePartPoint.ComponentType).getLabel(),
                        PDMComponentTypes.getEnumType(wFPCRoutePartPoint.ComponentType).getLabel(),
                        String.valueOf((int) fpcProductCom.ComponentNumber));
                return wResult;
            }

            //①先验证，后绑定
            for (String wSourceCode : wSourceCodeList) {
                wParams.clear();
                wParams.put("SourceCode", wSourceCode);
                wSFCAssemblyRecordList = mSFCAssemblyRecordService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                wSFCAssemblyRecordList.removeIf(p -> p.TargetComponentType == PDMComponentTypes.OutProduct.getValue());
                if (wSFCAssemblyRecordList != null && wSFCAssemblyRecordList.size() > 0) {
                    wResult.FaultCode += StringUtils.Format("来源工件【{0}】已绑定!", wSourceCode);
                    return wResult;
                }

                //查询工件记录
                wParams.clear();
                wParams.put("Code", wSourceCode);
                SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                    wResult.FaultCode += StringUtils.Format("【{0}】的工件记录不存在，请先自检!", wSourceCode);
                    return wResult;
                }
            }
            //②验证来源工件是否已绑定
            List<SFCAssemblyRecord> wToBindList = new ArrayList<>();
            for (String wSourceCode : wSourceCodeList) {
                wParams.clear();
                wParams.put("Code", wSourceCode);
                SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                        wErrorCode);

                //④批量组装
                SFCAssemblyRecord wSFCAssemblyRecord = new SFCAssemblyRecord(wSFCWorkpieceRecord.ComponentType,
                        wFPCRoutePartPoint.ComponentType,
                        wSourceCode, wTargetCode);
                wSFCAssemblyRecord.setUserInfo(wLoginUser);
                mSFCAssemblyRecordService.UpdatePo(wLoginUser, wSFCAssemblyRecord, wErrorCode);

                wToBindList.add(wSFCAssemblyRecord);

                //更新工件状态为`已完工`
                wSFCWorkpieceRecord.Status = SFCTaskStatus.Done.getValue();
                mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
            }

            //关联绑定
            if (wToBindList != null && wToBindList.size() > 0) {
                TaskTimerUtils.AddTask("UpdateWorkpieceRelaCode",
                        () -> SFCFrockBindServiceImpl.getInstance().UpdateWorkpieceRelaCode(wLoginUser, wToBindList)
                );
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 出厂条码绑定
     */
    private String FactoryBarcodeBinding(BMSEmployee wLoginUser, String wSourceCodes, String wTargetCode,
                                         OutResult<Integer> wErrorCode) {
        String wResult = "";
        try {
            //判断出厂码是否已绑定
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("TargetCode", wTargetCode);
            wParams.put("ComponentType", PDMComponentTypes.Product.getValue());
            wParams.put("TargetComponentType", PDMComponentTypes.OutProduct.getValue());
            List<SFCAssemblyRecord> wSFCAssemblyRecordList = mSFCAssemblyRecordService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCAssemblyRecordList != null && wSFCAssemblyRecordList.size() > 0) {
                wResult = StringUtils.Format("出厂码【{0}】已与产品码【{1}】绑定!!!", wTargetCode,
                        wSFCAssemblyRecordList.get(0).SourceCode);
                return wResult;
            }
            //根据条码（托盘|模具|芯子|成品|元件）获取成品码
            String wProductCode = SFCFrockBindServiceImpl.getInstance().SFC_GetProductCodeByBarCode(wLoginUser,
                    wSourceCodes).Result;
            if (StringUtils.isEmpty(wProductCode)) {
                wResult = StringUtils.Format("条码【{0}】未绑定产品码，请检查条码是否正确!!!", wSourceCodes,
                        wSFCAssemblyRecordList.get(0).TargetCode);
                return wResult;
            }
            //②查询产品码是否已绑定出厂码，若有记录，则报错
            wParams.clear();
            wParams.put("SourceCode", wProductCode);
            wParams.put("ComponentType", PDMComponentTypes.Product.getValue());
            wParams.put("TargetComponentType", PDMComponentTypes.OutProduct.getValue());
            wSFCAssemblyRecordList = mSFCAssemblyRecordService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCAssemblyRecordList != null && wSFCAssemblyRecordList.size() > 0) {
                wResult = StringUtils.Format("条码【{0}】绑定的产品码【{1}】已与出厂码【{2}】绑定!!!", wSourceCodes, wProductCode,
                        wSFCAssemblyRecordList.get(0).TargetCode);
                return wResult;
            }
            //③产品码绑定出厂码
            SFCAssemblyRecord wSFCAssemblyRecord = new SFCAssemblyRecord();
            wSFCAssemblyRecord.ComponentType = PDMComponentTypes.Product.getValue();
            wSFCAssemblyRecord.TargetComponentType = PDMComponentTypes.OutProduct.getValue();
            wSFCAssemblyRecord.SourceCode = wProductCode;
            wSFCAssemblyRecord.TargetCode = wTargetCode;
            wSFCAssemblyRecord.setUserInfo(wLoginUser);
            int wAffectRows = mSFCAssemblyRecordService.UpdatePo(wLoginUser, wSFCAssemblyRecord, wErrorCode);
            if (wAffectRows <= 0) {
                wResult = ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            //④查询产品码对应的工件记录
            wParams.clear();
            wParams.put("CodeEQ", wProductCode);
            SFCWorkpieceRecord wSFCWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCWorkpieceRecord == null || wSFCWorkpieceRecord.ID <= 0) {
                wResult = ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            //⑤维护工件记录上的出厂条码信息
            wSFCWorkpieceRecord.setUserInfo(wLoginUser);
            wSFCWorkpieceRecord.FPBarcode = wTargetCode;
            wAffectRows = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);
            if (wAffectRows <= 0) {
                wResult = ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
        } catch (Exception e) {
            log.error("FactoryBarcodeBinding", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_SaveTrayBindingComponents(BMSEmployee wLoginUser, String wTrayCode,
                                                                String wComponentCodes,
                                                                List<SFCTaskSelf> wSFCTaskSelfList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①验证托盘码是否正确
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("CodeEQ", wTrayCode);
            DMSFrockLedger wDMSFrockLedger = mDMSFrockLedgerService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wDMSFrockLedger == null || wDMSFrockLedger.ID <= 0 || wDMSFrockLedger.FrockType != AMSFrockType.TP.getValue()) {
                wResult.FaultCode += StringUtils.Format("托盘码【{0}】错误!", wTrayCode);
                return wResult;
            }
            //①验证托盘是否处于未绑定的状态
            String wFaultCode = CheckTrayCodeStatus(wLoginUser, wTrayCode);
            if (StringUtils.isNotEmpty(wFaultCode)) {
                wResult.FaultCode += wFaultCode;
                return wResult;
            }
            //②验证工件记录是否存在
            List<SFCWorkpieceRecord> wSFCWorkpieceRecordList = GetSFCWorkpieceRecordByComponentCodes(wLoginUser,
                    wComponentCodes);
            for (String wComponetCode : wComponentCodes.split(",")) {
                if (wSFCWorkpieceRecordList.stream().noneMatch(p -> p.Code.equals(wComponetCode))) {
                    wResult.FaultCode += StringUtils.Format("未查询到【{0}】的工件记录!", wComponetCode);
                    return wResult;
                }
            }
            //④新增绑定记录
            String[] wComponentCodeList = wComponentCodes.split(",");
            for (String wComponentCode : wComponentCodeList) {
                SFCFrockBindRecord wSFCFrockBindRecord = new SFCFrockBindRecord();
                wSFCFrockBindRecord.setUserInfo(wLoginUser);
                wSFCFrockBindRecord.ComponentCode = wComponentCode;
                wSFCFrockBindRecord.Status = SFCFrockBindRecordStatus.Default.getValue();
                wSFCFrockBindRecord.ComponentType = PDMComponentTypes.Component.getValue();
                wSFCFrockBindRecord.FrockLedgerID = wDMSFrockLedger.ID;
                wSFCFrockBindRecord.FrockLedgerCode = wDMSFrockLedger.Code;
                wSFCFrockBindRecord.Active = 1;
                //⑤保存
                mSFCFrockBindRecordService.UpdatePo(wLoginUser, wSFCFrockBindRecord, wErrorCode);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<DMSDeviceRealParameter>> SFC_QueryParamsByTaskID(BMSEmployee wLoginUser,
                                                                               int wTaskSelfID) {
        ServiceResult<List<DMSDeviceRealParameter>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①查询派工单
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wTaskSelfID);
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0) {
                return wResult;
            }
            //②查询工位信息
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.StationID);
            FMCStation wFMCStation = mFMCStationService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFMCStation == null || wFMCStation.ID <= 0 || wFMCStation.DeviceID == null || wFMCStation.DeviceID.size() == 0) {
                return wResult;
            }
            //③根据设备查询设备实时参数
            List<DMSDeviceRealParameter> wDMSDeviceRealParameterList =
                    DMSServiceImpl.getInstance().DMS_SelectDeviceRealParameterListByDeviceID(wLoginUser,
                            wFMCStation.DeviceID).Result;
            if (wDMSDeviceRealParameterList == null || wDMSDeviceRealParameterList.size() == 0) {
                return wResult;
            }
            //④根据工艺工序查询配置参数列表
            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            List<FPCRoutePartPointParameter> wFPCRoutePartPointParameterList =
                    mFPCRoutePartPointParameterService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
            if (wFPCRoutePartPointParameterList == null || wFPCRoutePartPointParameterList.size() == 0) {
                return wResult;
            }
            //⑤根据采集编号提取数据、返回前端
            wResult.Result =
                    wDMSDeviceRealParameterList.stream().filter(p -> wFPCRoutePartPointParameterList.stream().anyMatch(q -> q.CollectionNumber.contains(p.ParameterCode))).collect(Collectors.toList());

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryParamsByTaskID", e);
        }
        return wResult;
    }

    //endregion

    //region 产品追溯

    @Override
    public ServiceResult<List<SFCWorkpieceRecord>> SFC_WorkpieceRecordAll(BMSEmployee wLoginUser,
                                                                          String wMaterialBarcode,
                                                                          Calendar wStartTime, Calendar wEndTime,
                                                                          Map<String, Object> wParams,
                                                                          int wDeviceID, int wStationID,
                                                                          Pagination wPagination) {
        ServiceResult<List<SFCWorkpieceRecord>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //若通过物料条码查询，则除时间外的其他参数无效
            if (StringUtils.isNotEmpty(wMaterialBarcode)) {
                List<Integer> wIDList = SFCWorkpieceRecordDAO.getInstance().SFC_QueryListByBatchNumber(wLoginUser,
                        wMaterialBarcode, wStartTime, wEndTime, wPagination, wErrorCode);
                if (wIDList == null || wIDList.size() <= 0) {
                    return wResult;
                }

                wParams.clear();
                wResult.Result = mSFCWorkpieceRecordService.SelectPOList(wLoginUser, wIDList, wParams, wPagination,
                        wErrorCode);
            } else {
                //若工位ID存在，则通过工位查询工件ID集合
                List<Integer> wIDList = new ArrayList<>();
                if (wStationID > 0) {
                    wParams.clear();
                    wParams.put("CStartTime", wStartTime);
                    wParams.put("CEndTime", wEndTime);
                    wParams.put("StationID", wStationID);
                    List<SFCWorkpieceResume> sfcWorkpieceResumes = mSFCWorkpieceResumeService.SelectPOList(wLoginUser,
                            wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
                    wIDList =
                            sfcWorkpieceResumes.stream().map(p -> p.WorkpieceRecordID).distinct().collect(Collectors.toList());
                } else if (wDeviceID > 0) {
                    //通过设备查询工位
                    wParams.clear();
                    wParams.put("DeviceID", wDeviceID);
                    FMCStation fmcStation = mFMCStationService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                    if (fmcStation.ID > 0) {
                        wParams.clear();
                        wParams.put("CStartTime", wStartTime);
                        wParams.put("CEndTime", wEndTime);
                        wParams.put("StationID", fmcStation.ID);
                        List<SFCWorkpieceResume> sfcWorkpieceResumes =
                                mSFCWorkpieceResumeService.SelectPOList(wLoginUser,
                                        wParams, Pagination.getNewMaxSize(),
                                        wErrorCode);
                        wIDList =
                                sfcWorkpieceResumes.stream().map(p -> p.WorkpieceRecordID).distinct().collect(Collectors.toList());
                    }
                }

                wResult.Result = mSFCWorkpieceRecordService.SelectPOList(wLoginUser, wIDList, wParams, wPagination,
                        wErrorCode);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_WorkpieceRecordAll", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_QueryWorkpieceCirculateDetails(BMSEmployee wLoginUser, int wWorkpieceResumeID) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wWorkpieceResumeID);
            SFCWorkpieceResume wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCWorkpieceResume == null || wSFCWorkpieceResume.ID <= 0) {
                wResult.FaultCode += "1:" + ExceptionTypes.NotFound.getLabel();
                return wResult;
            }
            //①首检数据
            //②自检数据
            if (wSFCWorkpieceResume.SelfTaskIPTID > 0) {
                wParams.clear();
                wParams.put("ID", wSFCWorkpieceResume.SelfTaskIPTID);
                SFCTaskIPT wSelftTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wSelftTaskIPT == null || wSelftTaskIPT.ID <= 0 || wSelftTaskIPT.RoutePartPointID <= 0) {
                    wResult.FaultCode += "2:" + ExceptionTypes.NotFound.getLabel();
                    return wResult;
                }

                wParams.clear();
                wParams.put("RoutePartPointID", wSelftTaskIPT.RoutePartPointID);
                List<FPCRouteStep> wSelfFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                if (wSelfFPCRouteStepList == null || wSelfFPCRouteStepList.size() <= 0) {
                    wResult.FaultCode += "3:" + ExceptionTypes.NotFound.getLabel();
                    return wResult;
                }

                wParams.clear();
                wParams.put("TaskID", wSFCWorkpieceResume.SelfTaskIPTID);
                List<IPTValue> wSelfIPTValueList = mIPTValueService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                //首检项、首检值
                List<IPTValue> wFirstValueList =
                        wSelfIPTValueList.stream().filter(p -> p.IsFirstCheck == 1).sorted(Comparator.comparing(IPTValue::getStepOrder)).collect(Collectors.toList());

                //赋值工步信息
                AsignRouteStep(wLoginUser, wFirstValueList, wErrorCode);

                List<FPCRouteStep> wFirstStepList =
                        wSelfFPCRouteStepList.stream().filter(p -> wFirstValueList.stream().anyMatch(q -> q.RouteStepID == p.ID)).collect(Collectors.toList());
                wResult.CustomResult.put("FistCheckItems", wFirstStepList);
                wResult.CustomResult.put("FistCheckValues", wFirstValueList);
                //自检项、自检值
                List<FPCRouteStep> wSelfCheckItems =
                        wSelfFPCRouteStepList.stream().filter(p -> p.IsSelfCheck == 1
                                && wSelfIPTValueList.stream().anyMatch(q -> q.RouteStepID == p.ID)).collect(Collectors.toList());
                List<IPTValue> wSelfCheckValues =
                        wSelfIPTValueList.stream().filter(p -> wSelfCheckItems.stream().anyMatch(q -> q.ID == p.RouteStepID)).sorted(Comparator.comparing(IPTValue::getStepOrder)).collect(Collectors.toList());

                // 排序
                //赋值工步信息
                AsignRouteStep(wLoginUser, wSelfCheckValues, wErrorCode);

                wResult.CustomResult.put("SelfCheckItems", wSelfCheckItems);
                wResult.CustomResult.put("SelfCheckValues", wSelfCheckValues);
                //⑤物料数据
                wParams.clear();
                wParams.put("TaskIPTID", wSFCWorkpieceResume.SelfTaskIPTID);
                List<SFCMaterialLoadingRecord> wSFCMaterialLoadingRecordList =
                        mSFCMaterialLoadingRecordService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                                wErrorCode);
                wSFCMaterialLoadingRecordList.removeIf(p -> StringUtils.isEmpty(p.MaterialName));
                wResult.CustomResult.put("MaterialDatas", wSFCMaterialLoadingRecordList);
                //⑥环境数据、工艺数据、质量数据
                wParams.clear();
                wParams.put("TaskSelfID", wSelftTaskIPT.TaskSelfID);
                SFCTaskStartCheck wSFCTaskStartCheck = mSFCTaskStartCheckService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                wResult.CustomResult.put("StartCheckData", wSFCTaskStartCheck);
                //⑧工艺参数、环境参数、质量参数WriteParamItems
                if (wSFCTaskStartCheck != null && wSFCTaskStartCheck.ID > 0) {
                    wParams.clear();
                    wParams.put("TaskStartCheckID", wSFCTaskStartCheck.ID);
                    List<SFCTaskParamItem> wSFCTaskParamItemList = mSFCTaskParamItemService.SelectPOList(wLoginUser,
                            wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
                    wResult.CustomResult.put("WriteParamItems", wSFCTaskParamItemList);
                }
            }
            //③互检数据
            if (wSFCWorkpieceResume.MutualTaskIPTID > 0) {
                wParams.clear();
                wParams.put("ID", wSFCWorkpieceResume.MutualTaskIPTID);
                SFCTaskIPT wMutualTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wMutualTaskIPT == null || wMutualTaskIPT.ID <= 0 || wMutualTaskIPT.RoutePartPointID <= 0) {
                    wResult.FaultCode += "4:" + ExceptionTypes.NotFound.getLabel();
                    return wResult;
                }

                wParams.clear();
                wParams.put("RoutePartPointID", wMutualTaskIPT.RoutePartPointID);
                List<FPCRouteStep> wMutualFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                if (wMutualFPCRouteStepList == null || wMutualFPCRouteStepList.size() <= 0) {
                    wResult.FaultCode += "5:" + ExceptionTypes.NotFound.getLabel();
                    return wResult;
                }

                wParams.clear();
                wParams.put("TaskID", wSFCWorkpieceResume.MutualTaskIPTID);
                List<IPTValue> wMutualIPTValueList = mIPTValueService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                // 排序
                wMutualIPTValueList.sort(Comparator.comparing(IPTValue::getStepOrder));
                //赋值工步信息
                AsignRouteStep(wLoginUser, wMutualIPTValueList, wErrorCode);
                //互检项、互检值
                List<FPCRouteStep> wMutualCheckItems =
                        wMutualFPCRouteStepList.stream().filter(p -> wMutualIPTValueList.stream().anyMatch(q -> q.RouteStepID == p.ID)).collect(Collectors.toList());
                wResult.CustomResult.put("MutualCheckItems", wMutualCheckItems);
                wResult.CustomResult.put("MutualCheckValues", wMutualIPTValueList);
            }
            //④专检数据
            if (wSFCWorkpieceResume.SpecialTaskIPTID > 0) {
                wParams.clear();
                wParams.put("ID", wSFCWorkpieceResume.SpecialTaskIPTID);
                SFCTaskIPT wSpecialTaskIPT = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wSpecialTaskIPT == null || wSpecialTaskIPT.ID <= 0 || wSpecialTaskIPT.RoutePartPointID <= 0) {
                    wResult.FaultCode += "6:" + ExceptionTypes.NotFound.getLabel();
                    return wResult;
                }

                wParams.clear();
                wParams.put("RoutePartPointID", wSpecialTaskIPT.RoutePartPointID);
                List<FPCRouteStep> wSpecialFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                if (wSpecialFPCRouteStepList == null || wSpecialFPCRouteStepList.size() <= 0) {
                    wResult.FaultCode += "7:" + ExceptionTypes.NotFound.getLabel();
                    return wResult;
                }

                wParams.clear();
                wParams.put("TaskID", wSFCWorkpieceResume.SpecialTaskIPTID);
                List<IPTValue> wSpecialIPTValueList = mIPTValueService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                // 排序
                wSpecialIPTValueList.sort(Comparator.comparing(IPTValue::getStepOrder));
                //赋值工步信息
                AsignRouteStep(wLoginUser, wSpecialIPTValueList, wErrorCode);
                //专检项、专检值
                List<FPCRouteStep> wSpecialCheckItems =
                        wSpecialFPCRouteStepList.stream().filter(p -> wSpecialIPTValueList.stream().anyMatch(q -> q.RouteStepID == p.ID)).collect(Collectors.toList());
                wResult.CustomResult.put("SpecialCheckItems", wSpecialCheckItems);
                wResult.CustomResult.put("SpecialCheckValues", wSpecialIPTValueList);
            }
            //⑦报工单数据
            if (wSFCWorkpieceResume.TaskReportID > 0) {
                wParams.clear();
                wParams.put("ID", wSFCWorkpieceResume.TaskReportID);
                SFCTaskReport wSFCTaskReport = mSFCTaskReportService.SelectPO(wLoginUser, wParams,
                        wErrorCode);

                wParams.clear();
                wParams.put("TaskReportID", wSFCWorkpieceResume.TaskReportID);
                List<SFCTaskIPT> wReportIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                wResult.CustomResult.put("TaskReport", wSFCTaskReport);
                wResult.CustomResult.put("TaskReportItems", wReportIPTList);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryWorkpieceCirculateDetails", e);
        }
        return wResult;
    }

    /**
     * 赋值工步信息
     */
    private void AsignRouteStep(BMSEmployee wLoginUser, List<IPTValue> wIPTValueList, OutResult<Integer> wErrorCode) {
        try {
            if (wIPTValueList == null || wIPTValueList.size() <= 0) {
                return;
            }

            List<Integer> wRouteStepIDList =
                    wIPTValueList.stream().map(p -> p.RouteStepID).distinct().collect(Collectors.toList());
            Map<String, Object> wParams = new HashMap<String, Object>();
            List<FPCRouteStep> wFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wRouteStepIDList,
                    wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            Map<Integer, FPCRouteStep> wIDRouteStepMap =
                    wFPCRouteStepList.stream().collect(Collectors.toMap(FPCRouteStep::getID
                            , p -> p));
            for (IPTValue wIPTValue : wIPTValueList) {
                if (wIDRouteStepMap.containsKey(wIPTValue.RouteStepID)) {
                    wIPTValue.RouteStep = wIDRouteStepMap.get(wIPTValue.RouteStepID);
                }
            }
        } catch (Exception ex) {
            log.error("AsignRouteStep", ex);
        }
    }

    @Override
    public ServiceResult<Integer> SFC_WorkpieceRecordScrap(BMSEmployee wLoginUser,
                                                           SFCWorkpieceRecord wSFCWorkpieceRecord) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            wSFCWorkpieceRecord.Status = SFCTaskStatus.Scarp.getValue();
            wSFCWorkpieceRecord.setUserInfo(wLoginUser);
            wSFCWorkpieceRecord.ScarpID = wLoginUser.ID;
            wSFCWorkpieceRecord.ScarpTime = Calendar.getInstance();
            wResult.Result = mSFCWorkpieceRecordService.UpdatePo(wLoginUser, wSFCWorkpieceRecord, wErrorCode);

            //工件报废，子项也要设置为报废
            TaskTimerUtils.AddTask("SFC_WorkpieceRecordSubScrap",
                    () -> SFCFrockBindServiceImpl.getInstance().SFC_WorkpieceRecordSubScrap(wLoginUser,
                            wSFCWorkpieceRecord,
                            wErrorCode)
            );

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_WorkpieceRecordScrap", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<SFCWorkpieceRecord> SFC_QueryWorkpiecesJobDetails(BMSEmployee wLoginUser,
                                                                           int wWorkpieceRecordID) {
        ServiceResult<SFCWorkpieceRecord> wResult = new ServiceResult<>();
        wResult.Result = new SFCWorkpieceRecord();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①工件基本信息
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wWorkpieceRecordID);
            wResult.Result = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams, wErrorCode);
            //②自检记录
            List<SFCTaskIPT> wSelfTaskList = SFC_GetSelfTaskListByWorkpieceRecord(wLoginUser, wResult.Result,
                    wErrorCode);
            wResult.CustomResult.put("SelfTaskList", wSelfTaskList);
            //③互检记录
            List<SFCTaskIPT> wMutualTaskList = SFC_GetMutualTaskListByWorkpieceRecord(wLoginUser, wResult.Result,
                    wErrorCode);
            wResult.CustomResult.put("MutualTaskList", wMutualTaskList);
            //④专检记录
            List<SFCTaskIPT> wSpecialTaskList = SFC_GetSpecialTaskListByWorkpieceRecord(wLoginUser, wResult.Result,
                    wErrorCode);
            wResult.CustomResult.put("SpecialTaskList", wSpecialTaskList);
            //⑤报工记录
            List<SFCTaskReport> wTaskReportList = SFC_GetTaskReportListByWorkpieceRecord(wLoginUser, wResult.Result,
                    wSelfTaskList,
                    wErrorCode);
            wResult.CustomResult.put("TaskReportList", wTaskReportList);
            //⑥物料数据
            List<SFCMaterialLoadingRecord> wMaterialLoadingRecordList =
                    SFC_GetMaterialLoadingListByWorkpieceRecord(wLoginUser, wResult.Result, wSelfTaskList,
                            wErrorCode);
            wMaterialLoadingRecordList.removeIf(p -> StringUtils.isEmpty(p.MaterialName));
            wResult.CustomResult.put("MaterialLoadingRecordList", wMaterialLoadingRecordList);
            //⑦工艺参数、质量参数、环境参数
            List<List<SFCTaskParamItem>> wParamList =
                    SFC_GetTaskParamListByWorkpieceRecord(wLoginUser, wResult.Result, wSelfTaskList,
                            wErrorCode);
            //工艺参数替换为设备上的工艺参数
            List<DMSProcessRecord> wDMSProcessRecordList = new ArrayList<>();
            List<DMSDeviceParameter> wDMSDeviceParameterList = new ArrayList<>();
            if (wResult.Result.ID > 0 && StringUtils.isNotEmpty(wResult.Result.Code)) {
                //根据工件码获取设备采集过程记录
                wDMSProcessRecordList =
                        SFCFrockBindServiceImpl.getInstance().SFC_QueryrocessRecordListByWorkpieceCode(wLoginUser,
                                wResult.Result).Result;
                if (wDMSProcessRecordList != null && wDMSProcessRecordList.size() > 0) {
                    List<Integer> wDeviceIDList =
                            wDMSProcessRecordList.stream().map(p -> p.DeviceID).distinct().collect(Collectors.toList());
                    wParams.clear();
                    wParams.put("DataClass", 8);
                    wParams.put("Active", 1);
                    wParams.put("DeviceID", StringUtils.Join(",", wDeviceIDList));
                    wDMSDeviceParameterList = mDMSDeviceParameterService.SelectPOList(wLoginUser,
                            wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
                }
            }

            List<SFCTaskParamItem> wTechParamList =
                    SFCFrockBindServiceImpl.getInstance().SFC_ChangeParamList(wLoginUser,
                            wDMSProcessRecordList, wDMSDeviceParameterList).Result;

            wResult.CustomResult.put("TechParamList", wTechParamList);
            wResult.CustomResult.put("QualityParamList", wParamList.get(1));
            wResult.CustomResult.put("EnvironmentParamList", wParamList.get(2));
            //⑧组成数据
            List<SFCWorkpieceRecord> wComWorkpieceRecordList =
                    SFCFrockBindServiceImpl.getInstance().SFC_QueryComWorkpieceRecordList(wLoginUser, wResult.Result).Result;
            wResult.CustomResult.put("ComWorkpieceRecordList", wComWorkpieceRecordList);
            //⑨托盘、模具使用记录
            wParams.clear();
            wParams.put("WorkpieceRecordID", wWorkpieceRecordID);
            List<SFCToolUsageRecord> sfcToolUsageRecords = mSFCToolUsageRecordService.SelectPOList(wLoginUser,
                    wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
            wResult.CustomResult.put("ToolUsageRecordList", sfcToolUsageRecords);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    /**
     * 根据工件记录查询工艺、质量、环境参数
     */
    private List<List<SFCTaskParamItem>> SFC_GetTaskParamListByWorkpieceRecord(BMSEmployee wLoginUser,
                                                                               SFCWorkpieceRecord result,
                                                                               List<SFCTaskIPT> wSelfTaskList,
                                                                               OutResult<Integer> wErrorCode) {
        List<List<SFCTaskParamItem>> wObjectList = new ArrayList<>();
        try {
            if (wSelfTaskList == null || wSelfTaskList.size() == 0) {
                wObjectList.add(new ArrayList<>());
                wObjectList.add(new ArrayList<>());
                wObjectList.add(new ArrayList<>());
                return wObjectList;
            }

            //①根据自检获取派工任务IDs
            List<Integer> wTaskSelfIDList =
                    wSelfTaskList.stream().map(p -> p.TaskSelfID).distinct().collect(Collectors.toList());
            //②根据派工任务IDs获取开工检查单
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("TaskSelfID", StringUtils.Join(",", wTaskSelfIDList));
            List<SFCTaskStartCheck> wSFCTaskStartCheckList = mSFCTaskStartCheckService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCTaskStartCheckList == null || wSFCTaskStartCheckList.size() == 0) {
                wObjectList.add(new ArrayList<>());
                wObjectList.add(new ArrayList<>());
                wObjectList.add(new ArrayList<>());
                return wObjectList;
            }
            //③根据开工检查单ID获取参数
            wParams.clear();
            wParams.put("TaskStartCheckID", StringUtils.Join(",",
                    wSFCTaskStartCheckList.stream().map(p -> p.ID).collect(Collectors.toList())));
            List<SFCTaskParamItem> wSFCTaskParamItemList = mSFCTaskParamItemService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCTaskParamItemList == null || wSFCTaskParamItemList.size() == 0) {
                wObjectList.add(new ArrayList<>());
                wObjectList.add(new ArrayList<>());
                wObjectList.add(new ArrayList<>());
                return wObjectList;
            }
            //工艺参数
            List<SFCTaskParamItem> wTechList =
                    wSFCTaskParamItemList.stream().filter(p -> p.ParamType == FPCParamType.PROCESS_PARAMETERS.getValue()).collect(Collectors.toList());
            if (wTechList.size() > 0) {
                //按照工位+参数ID去重
                wTechList = wTechList.stream()
                        .collect(Collectors.collectingAndThen(Collectors.toCollection(
                                        () -> new TreeSet<>(Comparator.comparing(o -> o.getStationID() + ";" + o.getPartPointParamID()))),
                                ArrayList::new));
            }
            wObjectList.add(wTechList);
            //质量参数
            List<SFCTaskParamItem> wQualityList =
                    wSFCTaskParamItemList.stream().filter(p -> p.ParamType == FPCParamType.QUALITY_PARAMETERS.getValue()).collect(Collectors.toList());
            if (wQualityList.size() > 0) {
                //按照工位+参数ID去重
                wQualityList = wQualityList.stream()
                        .collect(Collectors.collectingAndThen(Collectors.toCollection(
                                        () -> new TreeSet<>(Comparator.comparing(o -> o.getStationID() + ";" + o.getPartPointParamID()))),
                                ArrayList::new));
            }
            wObjectList.add(wQualityList);
            //环境参数
            List<SFCTaskParamItem> wEnvironmentList =
                    wSFCTaskParamItemList.stream().filter(p -> p.ParamType == FPCParamType.ENVIRONMENT_PARAMETERS.getValue()).collect(Collectors.toList());
            if (wEnvironmentList.size() > 0) {
                //按照工位+参数ID去重
                wEnvironmentList = wEnvironmentList.stream()
                        .collect(Collectors.collectingAndThen(Collectors.toCollection(
                                        () -> new TreeSet<>(Comparator.comparing(o -> o.getStationID() + ";" + o.getPartPointParamID()))),
                                ArrayList::new));
                //查询自检单环境参数集合
                wParams.clear();
                wParams.put("TaskIPTID", StringUtils.Join(",",
                        wSelfTaskList.stream().map(p -> p.ID).collect(Collectors.toList())));
                List<SFCIPTEnvironmentRecord> sfciptEnvironmentRecords =
                        mSFCIPTEnvironmentRecordService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                                wErrorCode);
                //关联自检单的环境参数
                for (SFCTaskParamItem sfcTaskParamItem : wEnvironmentList) {
                    //找单子
                    Optional<SFCTaskIPT> first1 =
                            wSelfTaskList.stream().filter(p -> p.StationID == sfcTaskParamItem.StationID).findFirst();
                    if (!first1.isPresent()) {
                        continue;
                    }
                    //找参数
                    SFCTaskIPT sfcTaskIPT = first1.get();
                    Optional<SFCIPTEnvironmentRecord> first =
                            sfciptEnvironmentRecords.stream().filter(p -> p.TaskIPTID == sfcTaskIPT.ID).findFirst();
                    if (first.isPresent()) {
                        SFCIPTEnvironmentRecord sfciptEnvironmentRecord = first.get();
                        switch (sfcTaskParamItem.ParamName) {
                            case "温度":
                                sfcTaskParamItem.ParamValue = String.valueOf(sfciptEnvironmentRecord.Temperature);
                                break;
                            case "湿度":
                                sfcTaskParamItem.ParamValue = String.valueOf(sfciptEnvironmentRecord.Humidity);
                                break;
                            case "洁净度":
                                sfcTaskParamItem.ParamValue = String.valueOf(sfciptEnvironmentRecord.Cleanliness);
                                break;
                        }
                    }
                }
            }
            wObjectList.add(wEnvironmentList);
        } catch (Exception ex) {
            log.error("SFC_GetTaskParamListByWorkpieceRecord", ex);
        }
        return wObjectList;
    }

    /**
     * 根据工件记录查询物料记录
     */
    private List<SFCMaterialLoadingRecord> SFC_GetMaterialLoadingListByWorkpieceRecord(BMSEmployee wLoginUser,
                                                                                       SFCWorkpieceRecord result,
                                                                                       List<SFCTaskIPT> wSelfTaskList
            ,
                                                                                       OutResult<Integer> wErrorCode) {
        List<SFCMaterialLoadingRecord> wResult = new ArrayList<SFCMaterialLoadingRecord>();
        try {
            if (wSelfTaskList == null || wSelfTaskList.size() == 0) {
                return wResult;
            }

            String wSelfTaskIDs = StringUtils.Join(",",
                    wSelfTaskList.stream().map(p -> p.ID).collect(Collectors.toList()));

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("TaskIPTID", wSelfTaskIDs);
            wResult =
                    mSFCMaterialLoadingRecordService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
        } catch (Exception e) {
            log.error("SFC_GetMaterialLoadingListByWorkpieceRecord", e);
        }
        return wResult;
    }

    /**
     * 根据工件记录查询报工记录
     */
    private List<SFCTaskReport> SFC_GetTaskReportListByWorkpieceRecord(BMSEmployee wLoginUser,
                                                                       SFCWorkpieceRecord wSFCWorkpieceRecord,
                                                                       List<SFCTaskIPT> wSelfTaskList,
                                                                       OutResult<Integer> wErrorCode) {
        List<SFCTaskReport> wResult = new ArrayList<SFCTaskReport>();
        try {
            if (wSelfTaskList == null || wSelfTaskList.size() == 0) {
                return wResult;
            }

            List<Integer> wTaskReportIDList =
                    wSelfTaskList.stream().filter(p -> p.TaskReportID > 0).map(p -> p.TaskReportID).distinct().collect(Collectors.toList());
            if (wTaskReportIDList.size() == 0) {
                return wResult;
            }

            wResult = mSFCTaskReportService.SelectPOList(wLoginUser, wTaskReportIDList, new HashMap<String, Object>(),
                    Pagination.getNewMaxSize(),
                    wErrorCode);
        } catch (Exception e) {
            log.error("SFC_GetTaskReportListByWorkpieceRecord", e);
        }
        return wResult;
    }

    /**
     * 根据工件记录查询专检记录
     */
    private List<SFCTaskIPT> SFC_GetSpecialTaskListByWorkpieceRecord(BMSEmployee wLoginUser,
                                                                     SFCWorkpieceRecord wSFCWorkpieceRecord,
                                                                     OutResult<Integer> wErrorCode) {
        List<SFCTaskIPT> wResult = new ArrayList<SFCTaskIPT>();
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ComponentCode", wSFCWorkpieceRecord.Code);
            wParams.put("Active", 1);
            wParams.put("Type", FPCCheckType.SPECIAL_CHECK.getValue());
            wResult = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
        } catch (Exception e) {
            log.error("SFC_GetSpecialTaskListByWorkpieceRecord", e);
        }
        return wResult;
    }

    /**
     * 根据工件记录查询互检记录
     */
    private List<SFCTaskIPT> SFC_GetMutualTaskListByWorkpieceRecord(BMSEmployee wLoginUser,
                                                                    SFCWorkpieceRecord wSFCWorkpieceRecord
            , OutResult<Integer> wErrorCode) {
        List<SFCTaskIPT> wResult = new ArrayList<SFCTaskIPT>();
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ComponentCode", wSFCWorkpieceRecord.Code);
            wParams.put("Active", 1);
            wParams.put("Type", FPCCheckType.MUTUAL_CHECK.getValue());
            wResult = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
        } catch (Exception e) {
            log.error("SFC_GetMutualTaskListByWorkpieceRecord", e);
        }
        return wResult;
    }

    /**
     * 根据工件记录查询自检记录
     */
    private List<SFCTaskIPT> SFC_GetSelfTaskListByWorkpieceRecord(BMSEmployee wLoginUser,
                                                                  SFCWorkpieceRecord wSFCWorkpieceRecord,
                                                                  OutResult<Integer> wErrorCode) {
        List<SFCTaskIPT> wResult = new ArrayList<SFCTaskIPT>();
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ComponentCode", wSFCWorkpieceRecord.Code);
            wParams.put("Active", 1);
            wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
            wResult = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
        } catch (Exception e) {
            log.error("SFC_GetSelfTaskListByWorkpieceRecord", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<DMSDeviceParameter>> SFC_QueryParams(BMSEmployee wLoginUser, String wWorkpieceNo,
                                                                   int wRoutePartID, int wRoutePartPointID,
                                                                   int wStationID) {
        ServiceResult<List<DMSDeviceParameter>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            if (wStationID <= 0) {
                Map<String, Object> wParams = new HashMap<String, Object>();
                wParams.put("WorkpieceCode", wWorkpieceNo);
                wParams.put("RoutePartPointID", wRoutePartPointID);
                SFCWorkpieceResume wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser, wParams,
                        wErrorCode);
                if (wErrorCode.Result != 0) {
                    wResult.AddFaultCode(wErrorCode);
                    return wResult;
                }
                if (wSFCWorkpieceResume == null || wSFCWorkpieceResume.ID <= 0) {
                    return wResult;
                }
                wStationID = wSFCWorkpieceResume.StationID;
            }
            if (wStationID <= 0) {
                return wResult;
            }

            FMCStation wFMCStation = CacheUtils.GetCacheInfo(FMCStation.class, wStationID, "");
            if (wFMCStation == null || wFMCStation.ID <= 0) {
                return wResult;
            }

            List<Integer> wList = wFMCStation.DeviceID;
            if (wList == null || wList.size() == 0) {
                return wResult;
            }
            wList.removeIf(p -> p <= 0);
            if (wList.size() == 0) {
                return wResult;
            }

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("DeviceID", wList);
            wParams.put("DataClass", DMSDataClass.Params.getValue());
            wParams.put("Active", 1);
            wParams.put("DataGrad", 1);
            wResult.Result = mDMSDeviceParameterService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);

            //查询设备台账
            wParams.clear();
            List<DMSDeviceLedger> dmsDeviceLedgers = mDMSDeviceLedgerService.SelectPOList(wLoginUser, wList, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            wResult.CustomResult.put("DeviceList", dmsDeviceLedgers);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryParams", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<DMSDeviceParameter>> QueryParamsByStationIDs(BMSEmployee wLoginUser,
                                                                           List<Integer> wStationIDs
            , List<Integer> wDataClassList) {
        ServiceResult<List<DMSDeviceParameter>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            List<Integer> wList = new ArrayList<>();
//            String[] wStationStrs = wStationIDs.split(",");
            FMCStation wFMCStation;
            for (int wStationStr : wStationIDs) {
                wFMCStation = CacheUtils.GetCacheInfo(FMCStation.class, wStationStr, "");
                if (wFMCStation == null || wFMCStation.ID <= 0 || wFMCStation.DeviceID == null || wFMCStation.DeviceID.size() <= 0) {
                    continue;
                }
                wList.addAll(wFMCStation.DeviceID);
            }

            if (wList == null || wList.size() == 0) {
                return wResult;
            }
            wList.removeIf(p -> p <= 0);
            if (wList.size() == 0) {
                return wResult;
            }
            wList = wList.stream().distinct().collect(Collectors.toList());

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("DeviceID", wList);
            if (wDataClassList != null && wDataClassList.size() > 0) {
                wParams.put("DataClass", StringUtils.Join(",", wDataClassList));
            }
            wParams.put("Active", 1);
            wResult.Result = mDMSDeviceParameterService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryParams", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<List<Map<String, Object>>> QueryDeviceRecord(BMSEmployee wLoginUser, String wWorkpieceNo,
                                                                      int wRoutePartID, int wRoutePartPointID,
                                                                      int wStationID, int wDeviceID) {
        ServiceResult<List<Map<String, Object>>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("WorkpieceCode", wWorkpieceNo);
            wParams.put("RoutePartPointID", wRoutePartPointID);
            if (wStationID > 0) {
                wParams.put("StationID", wStationID);
            }
            SFCWorkpieceResume wSFCWorkpieceResume = mSFCWorkpieceResumeService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wErrorCode.Result != 0) {
                wResult.AddFaultCode(wErrorCode);
                return wResult;
            }
            if (wSFCWorkpieceResume == null || wSFCWorkpieceResume.ID <= 0) {
                return wResult;
            }

            DMSDeviceLedger wDMSDeviceLedger = CacheUtils.GetCacheInfo(DMSDeviceLedger.class, wDeviceID, "");
            if (wDMSDeviceLedger == null || wDMSDeviceLedger.ID <= 0 || StringUtils.isEmpty(wDMSDeviceLedger.Code)) {
                return wResult;
            }

            if (wSFCWorkpieceResume.SelfCheckTime.compareTo(wSFCWorkpieceResume.OnLineTime) <= 0) {
                return wResult;
            }

            wResult.Result = DMSDeviceRealParameterDAO.getInstance().DMS_SelectProcessRecordParams(wLoginUser,
                    wDMSDeviceLedger.Code,
                    wSFCWorkpieceResume.OnLineTime, wSFCWorkpieceResume.SelfCheckTime, wErrorCode);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryParams", e);
        }
        return wResult;
    }

    //endregion

    //region 设备接口集成

    @Override
    public ServiceResult<Integer> SFC_DeviceReport(BMSEmployee wLoginUser, SFCDeviceReport wSFCDeviceReport) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①查询派工任务
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCDeviceReport.TaskSelfID);
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            //查询工序任务
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            FPCRoutePartPoint wFPCRoutePartPoint = mFPCRoutePartPointService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFPCRoutePartPoint == null || wFPCRoutePartPoint.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            if (wFPCRoutePartPoint.ReportMode == SFCTaskReportMode.HAND_REPORT.getValue()) {
                //①根据派工任务ID查询待报工的自检记录
                wParams.clear();
                wParams.put("TaskSelfID", wSFCDeviceReport.TaskSelfID);
                wParams.put("Active", 1);
                wParams.put("Status", SFCTaskStatus.Done.getValue());
                wParams.put("Result", 1);
                wParams.put("IsReported", 0);
                List<SFCTaskIPT> wSFCTaskIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                        Pagination.getNewMaxSize(),
                        wErrorCode);
                //②若数量不满足要求，则报错
                if (wSFCTaskIPTList == null || wSFCTaskIPTList.size() < wSFCDeviceReport.ReportComNum) {
                    wResult.FaultCode += "报工失败：未查询到足够数量的工件自检单，请先做自检!!!";
                    return wResult;
                }
                //③提取满足数量的自检单
                wSFCTaskIPTList =
                        wSFCTaskIPTList.stream().limit((long) wSFCDeviceReport.ReportComNum).collect(Collectors.toList());

                wResult = SFCServiceImpl.getInstance().SFC_HandReport(wLoginUser, wSFCTaskIPTList);
            } else if (wFPCRoutePartPoint.ReportMode == SFCTaskReportMode.NUMBER_REPORT.getValue()) {
                SFCTaskReport wSFCTaskReport = new SFCTaskReport(wSFCTaskSelf.OrderID, wSFCTaskSelf.RoutePartID,
                        wSFCTaskSelf.RoutePartPointID, wSFCTaskSelf.ProductID,
                        wSFCTaskSelf.TaskPartID, wSFCTaskSelf.TaskPartPointID, wSFCTaskSelf.TaskPartPointDayID,
                        wSFCTaskSelf.ID, wSFCTaskSelf.WorkShopID, wSFCTaskSelf.WorkAreaID, wSFCTaskSelf.StationID,
                        wSFCTaskSelf.StationDate, wSFCTaskSelf.ShiftType, wSFCDeviceReport.ReportComNum,
                        SFCTaskReportStatus.TO_CONFIRM.getValue(), wFPCRoutePartPoint.ReportMode, 0f);
                wSFCTaskReport.OrderNo = wSFCTaskSelf.OrderNo;
                wSFCTaskReport.IsRepairPlan = wSFCTaskSelf.IsRepairPlan;
                wSFCTaskReport.ProductRepairID = wSFCTaskSelf.ProductRepairID;
                wResult = SFCServiceImpl.getInstance().SFC_NumberReport(wLoginUser, wSFCTaskReport);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_DeviceReport", e);
        }
        return wResult;
    }

    @Override
    public ServiceResult<Integer> SFC_DeviceUpdateProduct(BMSEmployee wLoginUser,
                                                          SFCStatusFeedback wSFCStatusFeedback) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①验证工单是否失效
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCStatusFeedback.ID);
            SFCTaskSelf wSFCTaskSelf = mSFCTaskSelfService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskSelf == null || wSFCTaskSelf.Active != 1) {
                wResult.FaultCode += "工单异常，工单失效!!!";
                return wResult;
            }
            //②验证订单是否关闭
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.OrderID);
            OMSOrder wOMSOrder = mOMSOrderService.SelectPO(wLoginUser, wParams, wErrorCode);
            if (wOMSOrder == null || wOMSOrder.ID <= 0 || wOMSOrder.Status == OMSOrderStatus.FinishedWork.getValue()) {
                wResult.FaultCode += "工单异常，订单已结束!!!";
                return wResult;
            }
            //③验证停线停序情况
            if (EXCServiceImpl.getInstance().EXC_IsRoutePartPointStop(wLoginUser, wSFCTaskSelf.OrderID,
                    wSFCTaskSelf.RoutePartPointID).Result) {
                wResult.FaultCode += "停序停线提示，工位禁止生产!!!";
                return wResult;
            }
            //④验证工单状态是否合理
            switch (SFCTaskStatus.getEnumType(wSFCStatusFeedback.Status)) {
                case Todo://待开工（不做处理）
                    break;
                case Doing://已开工
                    if (wSFCTaskSelf.Status != SFCTaskStatus.Todo.getValue()
                            && wSFCTaskSelf.Status != SFCTaskStatus.Suspend.getValue()
                            && wSFCTaskSelf.Status != SFCTaskStatus.Done.getValue()) {
                        wResult.FaultCode += StringUtils.Format("工单目前处于`{0}`状态， 不允许开工!!!",
                                wSFCTaskSelf.getStatusText());
                        return wResult;
                    }

                    //开工检验（创建开工检查单）
                    String wCheckMsg = SFC_StartCheck(wLoginUser, wSFCStatusFeedback, wSFCTaskSelf, wErrorCode);
                    if (StringUtils.isNotEmpty(wCheckMsg)) {
                        wResult.FaultCode += wCheckMsg;
                        return wResult;
                    }
                    break;
                case Suspend://暂停中
                    if (wSFCTaskSelf.Status != SFCTaskStatus.Doing.getValue()) {
                        wResult.FaultCode += StringUtils.Format("工单目前处于`{0}`状态， 不允许暂停!!!",
                                wSFCTaskSelf.getStatusText());
                        return wResult;
                    }
                    break;
                case Done://已完工
                    if (wSFCTaskSelf.Status != SFCTaskStatus.Doing.getValue()
                            && wSFCTaskSelf.Status != SFCTaskStatus.Suspend.getValue()) {
                        wResult.FaultCode += StringUtils.Format("工单目前处于`{0}`状态， 不允许完工!!!",
                                wSFCTaskSelf.getStatusText());
                        return wResult;
                    }
                    break;
                default:
                    break;
            }
            //⑤状态处理
            wSFCTaskSelf.setUserInfo(wLoginUser);
            wSFCTaskSelf.Status = wSFCStatusFeedback.Status;
            int wAffectRows = mSFCTaskSelfService.UpdatePo(wLoginUser, wSFCTaskSelf, wErrorCode);
            if (wAffectRows <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getValue();
                return wResult;
            }
            //⑥开工时，更新工位实时物料，其他状态直接更新即可
            if (wSFCTaskSelf.Status == SFCTaskStatus.Doing.getValue()
                    && wSFCStatusFeedback.MaterialList != null
                    && wSFCStatusFeedback.MaterialList.size() > 0) {
                //更新工位实时物料
                List<SFCStationMaterial> wSFCStationMaterialList = new ArrayList<>();
                for (SFCStatusFeedbackItem wSFCStatusFeedbackItem : wSFCStatusFeedback.MaterialList) {
                    SFCStationMaterial wSFCStationMaterial = new SFCStationMaterial(wSFCTaskSelf.StationID,
                            wSFCTaskSelf.RoutePartPointID, StringUtils.parseInt(wSFCStatusFeedbackItem.MaterialID),
                            wSFCStatusFeedbackItem.BatchBarCode);
                    //条码处理
                    wSFCStationMaterial.RawMaterialLot = wSFCStatusFeedbackItem.MaterialBatchNo;

                    wSFCStationMaterial.setUserInfo(wLoginUser);
                    wSFCStationMaterialList.add(wSFCStationMaterial);
                }
                mSFCStationMaterialService.UpdatePoList(wLoginUser, wSFCStationMaterialList, wErrorCode);
            }
            //完工，历史工单（相同订单、相同工序、相同班次的工单）
            if (wSFCTaskSelf.Status == SFCTaskStatus.Doing.getValue()) {
                SFCFrockBindServiceImpl.getInstance().SFC_FinishSameSelfTask(wLoginUser, wSFCTaskSelf);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_DeviceUpdateProduct", e);
        }
        return wResult;
    }

    /**
     * 开工检验（创建开工检查单）
     */
    private String SFC_StartCheck(BMSEmployee wLoginUser, SFCStatusFeedback wSFCStatusFeedback,
                                  SFCTaskSelf wSFCTaskSelf, OutResult<Integer> wErrorCode) {
        String wResult = "";
        try {
            //①根据工序BOM检查物料子项是否有批次信息（此处需将原料批次转换为物料批次）
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            List<MSSBOMItem> wMSSBOMItemList = mMSSBOMItemService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            for (MSSBOMItem wMSSBOMItem : wMSSBOMItemList) {
                //验证1物料是否存在2原料批次是否有值
                Optional<SFCStatusFeedbackItem> first =
                        wSFCStatusFeedback.MaterialList.stream().filter(p -> StringUtils.parseInt(p.MaterialID) == wMSSBOMItem.MaterialID
                                && StringUtils.isNotEmpty(p.MaterialBatchNo)).findFirst();
                if (first.isPresent()) {
                    SFCStatusFeedbackItem sfcStatusFeedbackItem = first.get();
                    //验证原料批次是否与物料批次绑定
                    wParams.clear();
                    wParams.put("MaterialID", sfcStatusFeedbackItem.MaterialID);
                    wParams.put("LotNo", sfcStatusFeedbackItem.MaterialBatchNo);
                    List<QMSMaterialCheck> qmsMaterialChecks = mQMSMaterialCheckService.SelectPOList(wLoginUser,
                            wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
                    if (qmsMaterialChecks == null || qmsMaterialChecks.size() <= 0) {
                        wResult = StringUtils.Format("物料【{0}-{1}】对应的原料批次【{2}】未绑定批次条码，请先做来料检验!", wMSSBOMItem.MaterialNo,
                                wMSSBOMItem.MaterialName, sfcStatusFeedbackItem.MaterialBatchNo);
                        return wResult;
                    } else {
                        sfcStatusFeedbackItem.BatchBarCode = qmsMaterialChecks.get(0).BatchRecordCode;
                    }
                } else {
                    wResult = StringUtils.Format("物料【{0}-{1}】无原料批次信息!", wMSSBOMItem.MaterialNo,
                            wMSSBOMItem.MaterialName);
                    return wResult;
                }
            }
            //查询设备采集参数
            wParams.clear();
            wParams.put("ID", wSFCTaskSelf.StationID);
            FMCStation wFMCStation = mFMCStationService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFMCStation.DeviceID == null || wFMCStation.DeviceID.size() == 0) {
                wResult = StringUtils.Format("工位【{0}-{1}】未绑定设备!", wFMCStation.Code, wFMCStation.Name);
                return wResult;
            }

            List<DMSDeviceRealParameter> wDMSDeviceRealParameterList =
                    DMSServiceImpl.getInstance().DMS_SelectDeviceRealParameterListByDeviceID(wLoginUser,
                            wFMCStation.DeviceID).Result;
            //②检查工艺参数（1、是否有填写值2、是否符合标准范围）
            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            wParams.put("ParamType", FPCParamType.PROCESS_PARAMETERS.getValue());
            List<FPCRoutePartPointParameter> wFPCRoutePartPointParameterList_Tech =
                    mFPCRoutePartPointParameterService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
            if (wFPCRoutePartPointParameterList_Tech.size() > 0) {
                for (FPCRoutePartPointParameter wFPCRoutePartPointParameter :
                        wFPCRoutePartPointParameterList_Tech) {
                    if (wDMSDeviceRealParameterList.stream().noneMatch(p -> wFPCRoutePartPointParameter.CollectionNumber.contains(p.ParameterCode))) {
                        wResult = StringUtils.Format("未获取到设备采集工艺参数【{0}】", wFPCRoutePartPointParameter.CollectionNumber);
                        return wResult;
                    }
                }
            }
            //③检查环境参数（1、是否有填写值2、是否符合标准范围）
            wParams.clear();
            wParams.put("StationID", wSFCTaskSelf.StationID);
            SFCStationEnvironment wSFCStationEnvironment =
                    mSFCStationEnvironmentService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
            if (wSFCStationEnvironment == null || wSFCStationEnvironment.ID <= 0) {
                wResult = StringUtils.Format("未获取到工位【{0}】的环境参数!", wSFCTaskSelf.StationName);
                return wResult;
            }

            wParams.clear();
            wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
            wParams.put("Active", 1);
            wParams.put("ParamType", FPCParamType.ENVIRONMENT_PARAMETERS.getValue());
            List<FPCRoutePartPointParameter> wFPCRoutePartPointParameterList_Envi =
                    mFPCRoutePartPointParameterService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                            wErrorCode);
            //①获取工艺参数合格与否
            boolean wTechResult = GetParamResult(wLoginUser, wDMSDeviceRealParameterList,
                    wFPCRoutePartPointParameterList_Tech, wErrorCode);
            //②获取环境参数合格与否
            boolean wEnviResult = GetEnviResult(wLoginUser, wFPCRoutePartPointParameterList_Envi,
                    wSFCStationEnvironment, wErrorCode);
            //④创建开工检查单
            SFCTaskStartCheck wSFCTaskStartCheck = new SFCTaskStartCheck();
            wSFCTaskStartCheck.TaskSelfID = wSFCTaskSelf.ID;
            wSFCTaskStartCheck.MaterialIsOk = 1;
            wSFCTaskStartCheck.QualificationIsOk = 1;
            wSFCTaskStartCheck.DeviceIsOk = 1;
            wSFCTaskStartCheck.ProcessParametersIsOk = wTechResult ? 1 : 2;
            wSFCTaskStartCheck.EnvironmentalParametersIsOk = wEnviResult ? 1 : 2;
            wSFCTaskStartCheck.QualityParametersIsOk = 1;
            int wAffectRows = mSFCTaskStartCheckService.UpdatePo(wLoginUser, wSFCTaskStartCheck, wErrorCode);
            if (wAffectRows > 0) {
                //⑤保存工艺参数
                SFC_SaveParams(wLoginUser, wDMSDeviceRealParameterList, wFPCRoutePartPointParameterList_Tech,
                        wSFCTaskStartCheck, wSFCTaskSelf, wErrorCode);
                //⑥保存环境参数
                SFC_SaveParams_Envri(wLoginUser, wSFCStationEnvironment, wFPCRoutePartPointParameterList_Envi,
                        wSFCTaskStartCheck, wSFCTaskSelf, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("SFC_StartCheck", ex);
        }
        return wResult;
    }

    /**
     * 保存环境参数
     */
    private void SFC_SaveParams_Envri(BMSEmployee wLoginUser, SFCStationEnvironment wSFCStationEnvironment,
                                      List<FPCRoutePartPointParameter> wFPCRoutePartPointParameterList_envi,
                                      SFCTaskStartCheck wSFCTaskStartCheck,
                                      SFCTaskSelf wSFCTaskSelf, OutResult<Integer> wErrorCode) {
        try {
            DMSDeviceRealParameter wDMSDeviceRealParameter;

            double wValue = 0;

            SFCTaskParamItem wSFCTaskParamItem;
            for (FPCRoutePartPointParameter wFPCRoutePartPointParameter : wFPCRoutePartPointParameterList_envi) {

                switch (wFPCRoutePartPointParameter.ParamName) {
                    case "温度":
                        wValue = wSFCStationEnvironment.Temperature;
                        break;
                    case "湿度":
                        wValue = wSFCStationEnvironment.Humidity;
                        break;
                    case "洁净度":
                        wValue = wSFCStationEnvironment.Cleanliness;
                        break;
                }

                wSFCTaskParamItem = new SFCTaskParamItem();
                wSFCTaskParamItem.TaskStartCheckID = wSFCTaskStartCheck.ID;
                wSFCTaskParamItem.PartPointParamID = wFPCRoutePartPointParameter.ID;
                wSFCTaskParamItem.ParamValue = String.valueOf(wValue);
                wSFCTaskParamItem.StationID = wSFCTaskSelf.StationID;
                wSFCTaskParamItem.CheckResult = GetParamResult_Envi(wLoginUser, wSFCStationEnvironment,
                        wFPCRoutePartPointParameter, wErrorCode) ? 1 : 2;
                mSFCTaskParamItemService.UpdatePo(wLoginUser, wSFCTaskParamItem, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("SFC_SaveParams_Envri", ex);
        }
    }

    private boolean GetEnviResult(BMSEmployee wLoginUser,
                                  List<FPCRoutePartPointParameter> wFPCRoutePartPointParameterList_Envi,
                                  SFCStationEnvironment wSFCStationEnvironment, OutResult<Integer> wErrorCode) {
        boolean wReslut = true;
        try {
            double wValue = 0;
            for (FPCRoutePartPointParameter wFPCRoutePartPointParameter : wFPCRoutePartPointParameterList_Envi) {
                switch (wFPCRoutePartPointParameter.ParamName) {
                    case "温度":
                        wValue = wSFCStationEnvironment.Temperature;
                        break;
                    case "湿度":
                        wValue = wSFCStationEnvironment.Humidity;
                        break;
                    case "洁净度":
                        wValue = wSFCStationEnvironment.Cleanliness;
                        break;
                }
                switch (FPCStandardType.getEnumType(wFPCRoutePartPointParameter.StandardType)) {
                    case Range:
                        wReslut =
                                wValue > wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case RangeEQ:
                        wReslut =
                                wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case RangeLTE:
                        wReslut =
                                wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case RangeGTE:
                        wReslut =
                                wValue > wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case LT:
                        wReslut = wValue < wFPCRoutePartPointParameter.StandardRight;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case GT:
                        wReslut = wValue > wFPCRoutePartPointParameter.StandardLeft;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case LTE:
                        wReslut = wValue <= wFPCRoutePartPointParameter.StandardRight;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case GTE:
                        wReslut = wValue >= wFPCRoutePartPointParameter.StandardLeft;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case EQ:
                        wReslut =
                                wValue == wFPCRoutePartPointParameter.StandardLeft || wValue == wFPCRoutePartPointParameter.StandardRight;
                        if (!wReslut) {
                            return wReslut;
                        }
                        break;
                    case Check:
                    case BadReason:
                    case ArrayNumber:
                    case ArrayText:
                    case ArrayNull:
                    case Number:
                    case Text:
                    case Combo:
                    default:
                        break;
                }
            }
        } catch (Exception ex) {
            log.error("GetEnviResult", ex);
        }
        return wReslut;
    }

    /**
     * 保存参数
     */
    private void SFC_SaveParams(BMSEmployee wLoginUser, List<DMSDeviceRealParameter> wDMSDeviceRealParameterList,
                                List<FPCRoutePartPointParameter> wParameterList, SFCTaskStartCheck wSFCTaskStartCheck,
                                SFCTaskSelf wSFCTaskSelf, OutResult<Integer> wErrorCode) {
        try {
            Map<String, DMSDeviceRealParameter> wParamCodeObjMap =
                    wDMSDeviceRealParameterList.stream().collect(Collectors.toMap(DMSDeviceRealParameter::getParameterCode
                            , p -> p));
            DMSDeviceRealParameter wDMSDeviceRealParameter;
            SFCTaskParamItem wSFCTaskParamItem;
            for (FPCRoutePartPointParameter wFPCRoutePartPointParameter : wParameterList) {
                if (!wParamCodeObjMap.containsKey(wFPCRoutePartPointParameter.CollectionNumber)) {
                    continue;
                }

                wDMSDeviceRealParameter =
                        wParamCodeObjMap.get(wFPCRoutePartPointParameter.CollectionNumber);
                wSFCTaskParamItem = new SFCTaskParamItem();
                wSFCTaskParamItem.TaskStartCheckID = wSFCTaskStartCheck.ID;
                wSFCTaskParamItem.PartPointParamID = wFPCRoutePartPointParameter.ID;
                wSFCTaskParamItem.ParamValue = wDMSDeviceRealParameter.ParameterValue;
                wSFCTaskParamItem.StationID = wSFCTaskSelf.StationID;
                wSFCTaskParamItem.CheckResult = GetParamResult(wLoginUser, wParamCodeObjMap,
                        wFPCRoutePartPointParameter, wErrorCode) ? 1 : 2;
                mSFCTaskParamItemService.UpdatePo(wLoginUser, wSFCTaskParamItem, wErrorCode);
            }
        } catch (Exception ex) {
            log.error("SFC_SaveParams", ex);
        }
    }

    /**
     * 判断参数是否合格
     */
    private boolean GetParamResult(BMSEmployee wLoginUser, List<DMSDeviceRealParameter> wDMSDeviceRealParameterList,
                                   List<FPCRoutePartPointParameter> wParameterList,
                                   OutResult<Integer> wErrorCode) {
        boolean wReslut = true;
        try {
            Map<String, DMSDeviceRealParameter> wParamCodeObjMap =
                    wDMSDeviceRealParameterList.stream().collect(Collectors.toMap(DMSDeviceRealParameter::getParameterCode
                            , p -> p));
            double wValue;
            for (FPCRoutePartPointParameter wFPCRoutePartPointParameter : wParameterList) {
                if (!wParamCodeObjMap.containsKey(wFPCRoutePartPointParameter.CollectionNumber)) {
                    continue;
                }

                DMSDeviceRealParameter wDMSDeviceRealParameter =
                        wParamCodeObjMap.get(wFPCRoutePartPointParameter.CollectionNumber);
                switch (FPCStandardType.getEnumType(wFPCRoutePartPointParameter.StandardType)) {
                    case Range:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut =
                                wValue > wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                        break;
                    case RangeEQ:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut =
                                wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                        break;
                    case RangeLTE:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut =
                                wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                        break;
                    case RangeGTE:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut =
                                wValue > wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                        break;
                    case LT:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut = wValue < wFPCRoutePartPointParameter.StandardRight;
                        break;
                    case GT:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut = wValue > wFPCRoutePartPointParameter.StandardLeft;
                        break;
                    case LTE:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut = wValue <= wFPCRoutePartPointParameter.StandardRight;
                        break;
                    case GTE:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut = wValue >= wFPCRoutePartPointParameter.StandardLeft;
                        break;
                    case EQ:
                        wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                        wReslut =
                                wValue == wFPCRoutePartPointParameter.StandardLeft || wValue == wFPCRoutePartPointParameter.StandardRight;
                        break;
                    case Check:
                    case BadReason:
                    case ArrayNumber:
                    case ArrayText:
                    case ArrayNull:
                    case Number:
                    case Text:
                    case Combo:
                    default:
                        break;
                }
            }
        } catch (Exception ex) {
            log.error("GetParamResult", ex);
        }
        return wReslut;
    }

    /**
     * 判断参数是否合格
     */
    private boolean GetParamResult(BMSEmployee wLoginUser, Map<String, DMSDeviceRealParameter> wParamCodeObjMap,
                                   FPCRoutePartPointParameter wFPCRoutePartPointParameter,
                                   OutResult<Integer> wErrorCode) {
        boolean wReslut = true;
        try {
            double wValue;
            DMSDeviceRealParameter wDMSDeviceRealParameter =
                    wParamCodeObjMap.get(wFPCRoutePartPointParameter.CollectionNumber);
            switch (FPCStandardType.getEnumType(wFPCRoutePartPointParameter.StandardType)) {
                case Range:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut =
                            wValue > wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                    break;
                case RangeEQ:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut =
                            wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                    break;
                case RangeLTE:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut =
                            wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                    break;
                case RangeGTE:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut =
                            wValue > wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                    break;
                case LT:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut = wValue < wFPCRoutePartPointParameter.StandardRight;
                    break;
                case GT:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut = wValue > wFPCRoutePartPointParameter.StandardLeft;
                    break;
                case LTE:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut = wValue <= wFPCRoutePartPointParameter.StandardRight;
                    break;
                case GTE:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut = wValue >= wFPCRoutePartPointParameter.StandardLeft;
                    break;
                case EQ:
                    wValue = StringUtils.parseDouble(wDMSDeviceRealParameter.ParameterValue);
                    wReslut =
                            wValue == wFPCRoutePartPointParameter.StandardLeft || wValue == wFPCRoutePartPointParameter.StandardRight;
                    break;
                case Check:
                case BadReason:
                case ArrayNumber:
                case ArrayText:
                case ArrayNull:
                case Number:
                case Text:
                case Combo:
                default:
                    break;
            }
        } catch (Exception ex) {
            log.error("GetParamResult", ex);
        }
        return wReslut;
    }

    /**
     * 判断参数是否合格
     */
    private boolean GetParamResult_Envi(BMSEmployee wLoginUser, SFCStationEnvironment wSFCStationEnvironment,
                                        FPCRoutePartPointParameter wFPCRoutePartPointParameter,
                                        OutResult<Integer> wErrorCode) {
        boolean wReslut = true;
        try {
            double wValue = 0;

            switch (wFPCRoutePartPointParameter.ParamName) {
                case "温度":
                    wValue = wSFCStationEnvironment.Temperature;
                    break;
                case "湿度":
                    wValue = wSFCStationEnvironment.Humidity;
                    break;
                case "洁净度":
                    wValue = wSFCStationEnvironment.Cleanliness;
                    break;
            }

            switch (FPCStandardType.getEnumType(wFPCRoutePartPointParameter.StandardType)) {
                case Range:
                    wReslut =
                            wValue > wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                    break;
                case RangeEQ:
                    wReslut =
                            wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                    break;
                case RangeLTE:
                    wReslut =
                            wValue >= wFPCRoutePartPointParameter.StandardLeft && wValue < wFPCRoutePartPointParameter.StandardRight;
                    break;
                case RangeGTE:
                    wReslut =
                            wValue > wFPCRoutePartPointParameter.StandardLeft && wValue <= wFPCRoutePartPointParameter.StandardRight;
                    break;
                case LT:
                    wReslut = wValue < wFPCRoutePartPointParameter.StandardRight;
                    break;
                case GT:
                    wReslut = wValue > wFPCRoutePartPointParameter.StandardLeft;
                    break;
                case LTE:
                    wReslut = wValue <= wFPCRoutePartPointParameter.StandardRight;
                    break;
                case GTE:
                    wReslut = wValue >= wFPCRoutePartPointParameter.StandardLeft;
                    break;
                case EQ:
                    wReslut =
                            wValue == wFPCRoutePartPointParameter.StandardLeft || wValue == wFPCRoutePartPointParameter.StandardRight;
                    break;
                case Check:
                case BadReason:
                case ArrayNumber:
                case ArrayText:
                case ArrayNull:
                case Number:
                case Text:
                case Combo:
                default:
                    break;
            }
        } catch (Exception ex) {
            log.error("GetParamResult_Envi", ex);
        }
        return wReslut;
    }

    @Override
    public ServiceResult<Integer> SFC_DeviceBatchSelfCheck(BMSEmployee wLoginUser,
                                                           List<SFCDeviceSelfData> wSFCDeviceSelfDataList) {
        ServiceResult<Integer> wResult = new ServiceResult<>(0);
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①查询派工任务
            List<Integer> wTaskSelfIDList =
                    wSFCDeviceSelfDataList.stream().map(p -> p.TaskSelfID).distinct().collect(Collectors.toList());
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("Active", 1);
            List<SFCTaskSelf> wSFCTaskSelfList = mSFCTaskSelfService.SelectPOList(wLoginUser, wTaskSelfIDList, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCTaskSelfList == null || wSFCTaskSelfList.size() == 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            Map<Integer, SFCTaskSelf> wIDTaskSelfMap =
                    wSFCTaskSelfList.stream().collect(Collectors.toMap(SFCTaskSelf::getID
                            , p -> p));
            //②查询工步列表
            String wRoutePartPointIDs = StringUtils.Join(",",
                    wSFCTaskSelfList.stream().map(p -> p.RoutePartPointID).distinct().collect(Collectors.toList()));
            wParams.clear();
            wParams.put("RoutePartPointID", wRoutePartPointIDs);
            wParams.put("Active", 1);
            wParams.put("IsSelfCheck", 1);
            List<FPCRouteStep> wFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            // 判断是否是需要保存的自检单（不提交）
            boolean wIsSave = wFPCRouteStepList.stream().anyMatch(p -> StringUtils.isEmpty(p.CollectionNumber));
            wFPCRouteStepList =
                    wFPCRouteStepList.stream().filter(p -> StringUtils.isNotEmpty(p.CollectionNumber)).collect(Collectors.toList());
            if (wFPCRouteStepList.size() == 0) {
                wResult.FaultCode += "批量自检失败：工步未配置，请检查工艺数据!!!";
                return wResult;
            }

            Map<Integer, List<FPCRouteStep>> wRoutePartPointStepListMap = wFPCRouteStepList.stream()
                    .collect(Collectors.groupingBy(FPCRouteStep::getRoutePartPointID));
            //③遍历创建自检单
            SFCTaskIPT wSFCTaskIPT;
            SFCTaskSelf wSFCTaskSelf;
            int wAffectRows;
            for (SFCDeviceSelfData wSFCDeviceSelfData : wSFCDeviceSelfDataList) {
                if (!wIDTaskSelfMap.containsKey(wSFCDeviceSelfData.TaskSelfID)) {
                    continue;
                }
                wSFCTaskSelf = wIDTaskSelfMap.get(wSFCDeviceSelfData.TaskSelfID);

                //判断有工件号的是否已做过自检，若做过，则跳过
                SFCTaskIPT wSFCTaskIPTCheck = null;
                if (StringUtils.isNotEmpty(wSFCDeviceSelfData.WorkpieceNo)) {
                    wParams.clear();
                    wParams.put("OrderID", wSFCTaskSelf.OrderID);
                    wParams.put("RoutePartPointID", wSFCTaskSelf.RoutePartPointID);
                    wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
                    wParams.put("ComponentCode", wSFCDeviceSelfData.WorkpieceNo);
                    wParams.put("IsRepairPlan", 0);
                    wSFCTaskIPTCheck = mSFCTaskIPTService.SelectPO(wLoginUser, wParams,
                            wErrorCode);
                }

                //根据采集编号得到IPTValueList
                ServiceResult<List<IPTValue>> wIPTValueSR = SFC_GetIPTValueSR(wLoginUser, wSFCDeviceSelfData,
                        wSFCTaskSelf, wRoutePartPointStepListMap, wErrorCode);
                if (StringUtils.isNotEmpty(wIPTValueSR.FaultCode)) {
                    wResult.FaultCode += wIPTValueSR.FaultCode;
                    return wResult;
                }

                if (wIPTValueSR.Result.size() == 0) {
                    wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                    return wResult;
                }

                //调用现有接口，实现设备自动自检（配置自动报工后可实现自动报工）
                if (StringUtils.isNotEmpty(wSFCDeviceSelfData.WorkpieceNo)) {
                    if (wSFCTaskIPTCheck != null && wSFCTaskIPTCheck.ID > 0) {
                        wParams.clear();
                        wParams.put("TaskID", wSFCTaskIPTCheck.ID);
                        List<IPTValue> iptValues = mIPTValueService.SelectPOList(wLoginUser, wParams,
                                Pagination.getNewMaxSize(),
                                wErrorCode);
                        for (IPTValue iptValue : iptValues) {
                            Optional<IPTValue> first =
                                    wIPTValueSR.Result.stream().filter(p -> p.RouteStepID == iptValue.RouteStepID).findFirst();
                            if (first.isPresent()) {
                                IPTValue iptValue1 = first.get();
                                iptValue.Value = iptValue1.Value;
                            }
                        }

                        //更新主单据检验结果
                        wSFCTaskIPTCheck.Result = wSFCDeviceSelfData.CheckResult;
                        mSFCTaskIPTService.UpdatePo(wLoginUser, wSFCTaskIPTCheck, wErrorCode);
                        //多次上传，同步工件追溯记录的加工结果
                        wParams.clear();
                        wParams.put("Code", wSFCDeviceSelfData.WorkpieceNo);
                        SFCWorkpieceRecord sfcWorkpieceRecord = mSFCWorkpieceRecordService.SelectPO(wLoginUser, wParams,
                                wErrorCode);
                        if (sfcWorkpieceRecord != null && sfcWorkpieceRecord.ID > 0) {
                            sfcWorkpieceRecord.CheckResult = wSFCDeviceSelfData.CheckResult;
                            mSFCWorkpieceRecordService.UpdatePo(wLoginUser, sfcWorkpieceRecord, wErrorCode);
                        }
                        //更新并提交检验值
                        ServiceResult<Integer> integerServiceResult =
                                SFCServiceImpl.getInstance().IPT_SubmitValue(wLoginUser
                                        , iptValues, true, wIsSave);
                        if (StringUtils.isNotEmpty(integerServiceResult.FaultCode)) {
                            wResult.FaultCode += integerServiceResult.FaultCode;
                            return wResult;
                        }

                    } else {
                        ServiceResult<SFCTaskIPT> sfcTaskIPTServiceResult =
                                SFCServiceImpl.getInstance().SFC_CreateTaskByWorkpieceNoAndTask(wLoginUser,
                                        wSFCTaskSelf.StationID
                                        , FPCCheckType.SELF_CHECK.getValue(), wSFCDeviceSelfData.WorkpieceNo,
                                        wSFCTaskSelf);
                        if (StringUtils.isNotEmpty(sfcTaskIPTServiceResult.FaultCode)) {
                            wResult.FaultCode += sfcTaskIPTServiceResult.FaultCode;
                            return wResult;
                        }

                        if (sfcTaskIPTServiceResult.Result == null || sfcTaskIPTServiceResult.Result.ID <= 0) {
                            wResult.FaultCode += StringUtils.Format("工件【{0}】创建自检单失败!", wSFCDeviceSelfData.WorkpieceNo);
                            return wResult;
                        }

                        //更新主单据检验结果
                        sfcTaskIPTServiceResult.Result.Result = wSFCDeviceSelfData.CheckResult;
                        mSFCTaskIPTService.UpdatePo(wLoginUser, sfcTaskIPTServiceResult.Result, wErrorCode);
                        //更新并提交检验值
                        wIPTValueSR.Result.forEach(p -> p.TaskID = sfcTaskIPTServiceResult.Result.ID);
                        ServiceResult<Integer> integerServiceResult =
                                SFCServiceImpl.getInstance().IPT_SubmitValue(wLoginUser
                                        , wIPTValueSR.Result, true, wIsSave);
                        if (StringUtils.isNotEmpty(integerServiceResult.FaultCode)) {
                            wResult.FaultCode += integerServiceResult.FaultCode;
                            return wResult;
                        }
                    }
                } else {
                    wResult.FaultCode += "批量自检失败：工件号不能为空!";
                    return wResult;
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_DeviceBatchSelfCheck", e);
        }
        return wResult;
    }

    /**
     * 将设备采集参数转换为IPTValue
     */
    private ServiceResult<List<IPTValue>> SFC_GetIPTValueSR(BMSEmployee wLoginUser,
                                                            SFCDeviceSelfData wSFCDeviceSelfData,
                                                            SFCTaskSelf wSFCTaskSelf,
                                                            Map<Integer, List<FPCRouteStep>> wRoutePartPointStepListMap, OutResult<Integer> wErrorCode) {
        ServiceResult<List<IPTValue>> wResult = new ServiceResult<>();
        wResult.Result = new ArrayList<>();
        try {
            if (!wRoutePartPointStepListMap.containsKey(wSFCTaskSelf.RoutePartPointID)) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }

            Map<String, String> wParameterNoValueMap =
                    wSFCDeviceSelfData.ParamValueList.stream().collect(Collectors.toMap(SFCDeviceSelfDataItem::getParameterNo
                            , SFCDeviceSelfDataItem::getParameterValue));

            List<FPCRouteStep> wFPCRouteStepList = wRoutePartPointStepListMap.get(wSFCTaskSelf.RoutePartPointID);
            for (FPCRouteStep wFPCRouteStep : wFPCRouteStepList) {
                String[] wCollectionNumbers = wFPCRouteStep.CollectionNumber.split(",");
                if (Arrays.stream(wCollectionNumbers).noneMatch(wParameterNoValueMap::containsKey)) {
                    wResult.FaultCode += StringUtils.Format("批量自检失败：工步【{0}】配置的采集参数【{1}】缺失", wFPCRouteStep.WorkContent,
                            wFPCRouteStep.CollectionNumber);
                    return wResult;
                }

                for (String wCollectionNumber : wCollectionNumbers) {
                    if (wParameterNoValueMap.containsKey(wCollectionNumber)) {
                        String wParamValue = wParameterNoValueMap.get(wCollectionNumber);
                        IPTValue wIPTValue = new IPTValue();
                        wIPTValue.RoutePartPointID = wFPCRouteStep.RoutePartPointID;
                        wIPTValue.RouteStepID = wFPCRouteStep.ID;
                        wIPTValue.Value = wParamValue;
                        wIPTValue.Result = SFC_GetCheckResult(wLoginUser, wParamValue, wFPCRouteStep);
                        wResult.Result.add(wIPTValue);
                    }
                }
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_GetIPTValueSR", e);
        }
        return wResult;
    }

    /**
     * 判定是否合格
     */
    private int SFC_GetCheckResult(BMSEmployee wLoginUser, String wParamValue, FPCRouteStep wFPCRouteStep) {
        int wReslut = 1;
        try {
            double wValue;
            switch (FPCStandardType.getEnumType(wFPCRouteStep.StandardType)) {
                case Range:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue > wFPCRouteStep.StandardLeft && wValue < wFPCRouteStep.StandardRight) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case RangeEQ:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue >= wFPCRouteStep.StandardLeft && wValue <= wFPCRouteStep.StandardRight) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case RangeLTE:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue >= wFPCRouteStep.StandardLeft && wValue < wFPCRouteStep.StandardRight) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case RangeGTE:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue > wFPCRouteStep.StandardLeft && wValue <= wFPCRouteStep.StandardRight) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case LT:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue < wFPCRouteStep.StandardRight) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case GT:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue > wFPCRouteStep.StandardLeft) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case LTE:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue <= wFPCRouteStep.StandardRight) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case GTE:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue >= wFPCRouteStep.StandardLeft) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case EQ:
                    wValue = StringUtils.parseDouble(wParamValue);
                    if (wValue == wFPCRouteStep.StandardLeft || wValue == wFPCRouteStep.StandardRight) {
                        wReslut = 1;
                    } else {
                        wReslut = 2;
                    }
                    break;
                case Check:
                case BadReason:
                case ArrayNumber:
                case ArrayText:
                case ArrayNull:
                case Number:
                case Text:
                case Combo:
                default:
                    break;
            }
        } catch (Exception ex) {
            log.error("SFC_GetCheckResult", ex);
        }
        return wReslut;
    }

    //endregion

    //region 出厂试验单打印
    @Override
    public ServiceResult<SFCFactoryTest> SFC_QueryFactoryTest(BMSEmployee wLoginUser, String wWorkpieceNos) {
        ServiceResult<SFCFactoryTest> wResult = new ServiceResult<>();
        wResult.Result = new SFCFactoryTest();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①根据工件号查询出厂试验自检单
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ComponentCode", wWorkpieceNos);
            wParams.put("Type", FPCCheckType.SELF_CHECK.getValue());
            wParams.put("Status", SFCTaskStatus.Done.getValue());
            List<SFCTaskIPT> wSFCTaskIPTList = mSFCTaskIPTService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wSFCTaskIPTList == null || wSFCTaskIPTList.size() == 0) {
                return wResult;
            }
            //②判断是否同属一个订单，否则报错
            List<Integer> wOrderIDList =
                    wSFCTaskIPTList.stream().map(p -> p.OrderID).distinct().collect(Collectors.toList());
            if (wOrderIDList.size() > 1) {
                wResult.FaultCode += StringUtils.Format("工件【{0}】不属于同一订单,请检查数据!!!", wWorkpieceNos);
                return wResult;
            }
            //③查询工步
            String wRoutePartPointIDs = StringUtils.Join(",",
                    wSFCTaskIPTList.stream().map(p -> p.RoutePartPointID).distinct().collect(Collectors.toList()));
            wParams.clear();
            wParams.put("RoutePartPointID", wRoutePartPointIDs);
            wParams.put("Active", 1);
            wParams.put("IsPrint", 1);
            List<FPCRouteStep> wFPCRouteStepList = mFPCRouteStepService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            if (wFPCRouteStepList == null || wFPCRouteStepList.size() <= 0) {
                return wResult;
            }
            //③查询Value集合
            List<Integer> wTaskIDList = wSFCTaskIPTList.stream().map(p -> p.ID).collect(Collectors.toList());
            wParams.clear();
            wParams.put("TaskID", StringUtils.Join(",", wTaskIDList));
            wParams.put("IsPrint", 1);
            List<IPTValue> wIPTValueList = mIPTValueService.SelectPOList(wLoginUser, wParams,
                    Pagination.getNewMaxSize(),
                    wErrorCode);
            Map<Integer, List<IPTValue>> wTaskIDValueListMap = wIPTValueList.stream()
                    .collect(Collectors.groupingBy(IPTValue::getTaskID));
            //④渲染数据
            List<SFCFactoryTestItem> wItemList = new ArrayList<>();
            String[] wWorkpieceNoList = wWorkpieceNos.split(",");

            Map<String, List<SFCTaskIPT>> wComponentCodeIPTListMap = wSFCTaskIPTList.stream()
                    .collect(Collectors.groupingBy(SFCTaskIPT::getComponentCode));

            Calendar testDate = CalendarTool.getBasicDate();
            String checkResult = "合格";
            for (String wWorkpieceNo : wWorkpieceNoList) {
                if (!wComponentCodeIPTListMap.containsKey(wWorkpieceNo)) {
                    continue;
                }
                //①获取工件对应的检验单
                List<SFCTaskIPT> sfcTaskIPTS = wComponentCodeIPTListMap.get(wWorkpieceNo);
//                ②按照工序顺序排序
//                sfcTaskIPTS.sort(Comparator.comparing(SFCTaskIPT::getTaskPartPointID));
                //③筛选打印的参数，依次赋值
                List<IPTValue> wIPTValueList_ = new ArrayList<>();
                for (SFCTaskIPT sfcTaskIPT : sfcTaskIPTS) {
                    if (!wTaskIDValueListMap.containsKey(sfcTaskIPT.ID)) {
                        continue;
                    }

                    List<IPTValue> iptValues = wTaskIDValueListMap.get(sfcTaskIPT.ID);
                    testDate = sfcTaskIPT.CreateTime;
                    if (iptValues.stream().anyMatch(p -> p.Result != 1)) {
                        checkResult = "不合格";
                    }

                    wIPTValueList_.addAll(iptValues);
                }
                wIPTValueList_.sort(Comparator.comparing(IPTValue::getStepOrder));
                List<String> valueList =
                        wIPTValueList_.stream().map(p -> p.Value).collect(Collectors.toList());
                //添加数据
                SFCFactoryTestItem wSFCFactoryTestItem = new SFCFactoryTestItem(testDate, wWorkpieceNo, valueList,
                        checkResult);
                wItemList.add(wSFCFactoryTestItem);
            }

            //查询编码表，获取编码
            SFCFactoryTestCode wSFCFactoryTestCode = SFC_GetSFCFactoryTestCodeBywWorkpieceNos(wLoginUser,
                    wWorkpieceNos, wErrorCode);
            if (wSFCFactoryTestCode.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.Logic.getLabel();
                return wResult;
            }

            //获取环境参数中的温度和湿度
            List<SFCTaskParamItem> wTaskParamItemList = SFC_GetTaskParamItemListByTaskIPT(wLoginUser,
                    wSFCTaskIPTList.get(0), wErrorCode);
            float wTemperature = SFC_GetTemperature(wLoginUser, wTaskParamItemList, wErrorCode);
            float wHumidity = SFC_GetHumidity(wLoginUser, wTaskParamItemList, wErrorCode);

            wResult.Result = new SFCFactoryTest(wSFCFactoryTestCode.TableCode, wSFCFactoryTestCode.Code,
                    wSFCTaskIPTList.get(0).ProductName,
                    wSFCTaskIPTList.get(0).ProductCode, wTemperature,
                    wHumidity, Calendar.getInstance(), wItemList);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_QueryFactoryTest", e);
        }
        return wResult;
    }

    /**
     * 获取湿度
     */
    private float SFC_GetHumidity(BMSEmployee wLoginUser, List<SFCTaskParamItem> wTaskParamItemList,
                                  OutResult<Integer> wErrorCode) {
        float wResult = 35;
        try {
            if (wTaskParamItemList == null || wTaskParamItemList.size() == 0) {
                return wResult;
            }

            if (wTaskParamItemList.stream().anyMatch(p -> p.ParamName.equals("湿度"))) {
                wResult =
                        StringUtils.parseFloat(wTaskParamItemList.stream().filter(p -> p.ParamName.equals("湿度")).findFirst().get().ParamValue);
            }
        } catch (Exception ex) {
            log.error("SFC_GetTemperature", ex);
        }
        return wResult;
    }

    /**
     * 获取温度
     */
    private float SFC_GetTemperature(BMSEmployee wLoginUser, List<SFCTaskParamItem> wTaskParamItemList,
                                     OutResult<Integer> wErrorCode) {
        float wResult = 18;
        try {
            if (wTaskParamItemList == null || wTaskParamItemList.size() == 0) {
                return wResult;
            }

            if (wTaskParamItemList.stream().anyMatch(p -> p.ParamName.equals("温度"))) {
                wResult =
                        StringUtils.parseFloat(wTaskParamItemList.stream().filter(p -> p.ParamName.equals("温度")).findFirst().get().ParamValue);
            }
        } catch (Exception ex) {
            log.error("SFC_GetTemperature", ex);
        }
        return wResult;
    }

    /**
     * 根据自检单获取开工检查中环境参数列表
     */
    private List<SFCTaskParamItem> SFC_GetTaskParamItemListByTaskIPT(BMSEmployee wLoginUser, SFCTaskIPT wSFCTaskIPT,
                                                                     OutResult<Integer> wErrorCode) {
        List<SFCTaskParamItem> wResult = new ArrayList<SFCTaskParamItem>();
        try {
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("TaskSelfID", wSFCTaskIPT.TaskSelfID);
            SFCTaskStartCheck wSFCTaskStartCheck = mSFCTaskStartCheckService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCTaskStartCheck == null || wSFCTaskStartCheck.ID <= 0) {
                return wResult;
            }

            wParams.clear();
            wParams.put("TaskStartCheckID", wSFCTaskStartCheck.ID);
            wParams.put("ParamType", FPCParamType.ENVIRONMENT_PARAMETERS.getValue());
            wResult = mSFCTaskParamItemService.SelectPOList(wLoginUser, wParams, Pagination.getNewMaxSize(),
                    wErrorCode);
        } catch (Exception e) {
            log.error("SFC_GetTaskParamItemListByTaskIPT", e);
        }
        return wResult;

    }

    /**
     * 根据工件获取编码表
     */
    private SFCFactoryTestCode SFC_GetSFCFactoryTestCodeBywWorkpieceNos(BMSEmployee wLoginUser, String wWorkpieceNos,
                                                                        OutResult<Integer> wErrorCode) {
        SFCFactoryTestCode wResult = new SFCFactoryTestCode();
        try {
            if (StringUtils.isEmpty(wWorkpieceNos)) {
                return wResult;
            }

            if (wWorkpieceNos.contains(",")) {
                wWorkpieceNos = wWorkpieceNos.replaceAll(",", "");
            }

            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("WorkpieceNos", wWorkpieceNos);
            SFCFactoryTestCode wSFCFactoryTestCode = mSFCFactoryTestCodeService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wSFCFactoryTestCode != null && wSFCFactoryTestCode.ID > 0) {
                wResult = wSFCFactoryTestCode;
            } else {
                wResult = new SFCFactoryTestCode(wWorkpieceNos);
                int wAffecRows = mSFCFactoryTestCodeService.UpdatePo(wLoginUser, wResult, wErrorCode);
                if (wAffecRows > 0) {
                    wResult.TableCode = wResult.Code.replace("JSWJ", "CXWJ");
                    mSFCFactoryTestCodeService.UpdatePo(wLoginUser, wResult, wErrorCode);
                }
            }
        } catch (Exception ex) {
            log.error("SFC_GetSFCFactoryTestCodeBywWorkpieceNos", ex);
        }
        return wResult;
    }

    @Override
    public ServiceResult<String> SFC_ExportFactoryTest(BMSEmployee wLoginUser, SFCFactoryTest wSFCFactoryTest) {
        ServiceResult<String> wResult = new ServiceResult<>();
        wResult.Result = "";
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①获取替换的参数
            List<ExcelCellData> wExcelCellDataList = SFC_GetExcelCellDataList(wLoginUser, wSFCFactoryTest, wErrorCode);
            //②获取行数据
            List<String> wData = SFC_GetExcelLineData(wLoginUser, wSFCFactoryTest, wErrorCode);
            //③输出打印Excel
            String wTemplatePath = StringUtils.Format("{0}FactoryTestTemplate.xlsx", Constants.getConfigPath());
            int wStartRow = 11;

            String wFileName = StringUtils.Format("出厂试验单{0}.xlsx",
                    new SimpleDateFormat("yyyyMMddHHmmss").format(Calendar.getInstance().getTime()));

            String wOutputPath = SFC_GetOutputPath(wLoginUser, wFileName, wErrorCode);
            ExcelUtil.fillTemplate(wTemplatePath, wStartRow, wData, wOutputPath, wExcelCellDataList);

            wResult.Result = StringUtils.Format("/{0}/export/{1}",
                    Configuration.readConfigString("project.name", "application"), wFileName);

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error("SFC_ExportFactoryTest", e);
        }
        return wResult;
    }

    /**
     * 获取待替换的参数
     */
    private List<ExcelCellData> SFC_GetExcelCellDataList(BMSEmployee wLoginUser, SFCFactoryTest wSFCFactoryTest,
                                                         OutResult<Integer> wErrorCode) {
        List<ExcelCellData> wResult = new ArrayList<>();
        try {
            //表码
            ExcelCellData wExcelCellData = new ExcelCellData(3, 0, StringUtils.Format("表码：{0}",
                    wSFCFactoryTest.TableCode));
            wResult.add(wExcelCellData);
            //报告编号
            wExcelCellData = new ExcelCellData(3, 23, StringUtils.Format("报告编号：{0}",
                    wSFCFactoryTest.ReportNumber));
            wResult.add(wExcelCellData);
            //产品名称
            wExcelCellData = new ExcelCellData(4, 0, StringUtils.Format("产品名称：{0}",
                    wSFCFactoryTest.ProductName));
            wResult.add(wExcelCellData);
            //试区温度、湿度
            wExcelCellData = new ExcelCellData(4, 23, StringUtils.Format("试区温度：{0}℃   湿度：{1}％",
                    String.valueOf(wSFCFactoryTest.Temperature).replaceAll("[.0]+$", ""),
                    String.valueOf(wSFCFactoryTest.Humidity).replaceAll("[.0]+$", "")));
            wResult.add(wExcelCellData);
            //产品型号
            wExcelCellData = new ExcelCellData(5, 0, StringUtils.Format("产品型号：{0}",
                    wSFCFactoryTest.ProductNo));
            wResult.add(wExcelCellData);
            //出厂日期
            SimpleDateFormat wSDF = new SimpleDateFormat("yyyy 年 MM 月 dd 日");
            String wCurrentTime = wSDF.format(wSFCFactoryTest.ProductDate.getTime());
            wExcelCellData = new ExcelCellData(5, 23, StringUtils.Format("出厂日期：{0}",
                    wCurrentTime));
            wResult.add(wExcelCellData);
            //E9
            //E11
            //I9
            //I11
            //K9
            //N9
            //Q7
            //T7
            //U9
            //X9
            //U7
            //AA8
            //AD9
        } catch (Exception ex) {
            log.error("SFC_GetExcelCellDataList", ex);
        }
        return wResult;
    }

    /**
     * 获取行数据
     */
    private List<String> SFC_GetExcelLineData(BMSEmployee wLoginUser, SFCFactoryTest wSFCFactoryTest,
                                              OutResult<Integer> wErrorCode) {
        List<String> wResult = new ArrayList<>();
        try {
            int wIndex = 1;
            SimpleDateFormat wSDF = new SimpleDateFormat("yyyy/MM/dd");
            for (SFCFactoryTestItem wSFCFactoryTestItem : wSFCFactoryTest.ItemList) {
                List<String> wRowList = new ArrayList<>(Arrays.asList(String.valueOf(wIndex++),
                        wSDF.format(wSFCFactoryTestItem.TestDate.getTime()), wSFCFactoryTestItem.CapacitorNumber));
                wRowList.addAll(wSFCFactoryTestItem.ValueList);
                wRowList.add(wSFCFactoryTestItem.CheckResult);
                wResult.add(StringUtils.Join(",", wRowList));
            }
        } catch (Exception ex) {
            log.error("SFC_GetExcelLineData", ex);
        }
        return wResult;
    }

    /**
     * 获取导出文件的路径
     */
    private String SFC_GetOutputPath(BMSEmployee wLoginUser, String wFileName, OutResult<Integer> wErrorCode) {
        String wResult = "";
        try {
            String wExportFolder = StringUtils.Format("{0}export/", Constants.getStaticPath());

            Path folder = Paths.get(wExportFolder);

            if (!Files.exists(folder)) {
                Files.createDirectories(folder);
            }

            wResult = StringUtils.Format("{0}{1}", wExportFolder, wFileName);
        } catch (Exception ex) {
            log.error("SFC_GetOutputPath", ex);
        }
        return wResult;
    }

    @Override
    public ServiceResult<BCMRuleCode> SFC_CreateBarCodeByTask(BMSEmployee wLoginUser, SFCTaskSelf wSFCTaskSelf) {
        ServiceResult<BCMRuleCode> wResult = new ServiceResult<>();
        wResult.Result = new BCMRuleCode();
        try {
            OutResult<Integer> wErrorCode = new OutResult<>(0);

            //①查询产品类型
            Map<String, Object> wParams = new HashMap<String, Object>();
            wParams.put("ID", wSFCTaskSelf.ProductID);
            wParams.put("Active", 1);
            FPCProduct wFPCProduct = mFPCProductService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wFPCProduct == null || wFPCProduct.ID <= 0) {
                wResult.FaultCode += ExceptionTypes.DBSQL.getLabel();
                return wResult;
            }
            //②根据产品类型和产出类型查询激活的条码规则
            wParams.clear();
            wParams.put("ComponentType", wSFCTaskSelf.ComponentType);
            wParams.put("ProductTypeID", wFPCProduct.ProductTypeID);
            wParams.put("Active", 1);
            BCMRule wBCMRule = mBCMRuleService.SelectPO(wLoginUser, wParams,
                    wErrorCode);
            if (wBCMRule == null || wBCMRule.ID <= 0) {
                wResult.FaultCode += "生成条码失败：未查询到已激活的条码规则，请检查条码规则配置!";
                return wResult;
            }
            //③根据条码规则创建一条条码
            List<BCMRuleCode> wBCMRuleCodeList = BCMServiceImpl.getInstance().BCM_CreateRuleCodeByRule(wLoginUser,
                    null, wBCMRule, 1, true).Result;
            if (wBCMRuleCodeList != null && wBCMRuleCodeList.size() > 0) {
                wResult.Result = wBCMRuleCodeList.get(0);
            }

            wResult.setFaultCode(ExceptionTypes.getEnumType(wErrorCode.Result).getLabel());
        } catch (Exception e) {
            wResult.FaultCode += e.toString();
            log.error(e.toString());
        }
        return wResult;
    }

    //endregion
}
